Skip to content

Commit 3897c2c

Browse files
authored
feat(client): add preview functionality for emoji hover (#3309)
1 parent a558245 commit 3897c2c

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

packages/client/src/components/CommentBox.vue

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useDebounceFn, useEventListener, watchImmediate } from '@vueuse/core';
33
import type { WalineComment, WalineCommentData, UserInfo } from '@waline/api';
44
import { addComment, login, updateComment } from '@waline/api';
55
import autosize from 'autosize';
6-
import type { DeepReadonly } from 'vue';
6+
import type { DeepReadonly, CSSProperties } from 'vue';
77
import {
88
computed,
99
inject,
@@ -88,6 +88,35 @@ const gifSearchRef = useTemplateRef<HTMLInputElement>('gif-search');
8888
const emoji = ref<DeepReadonly<WalineEmojiConfig>>({ tabs: [], map: {} });
8989
const emojiTabIndex = ref(0);
9090
const showEmoji = ref(false);
91+
const previewEmoji = ref('');
92+
const previewStyle = ref<CSSProperties>({});
93+
let leaveTimer: ReturnType<typeof setTimeout>;
94+
95+
const onEmojiHover = (event: MouseEvent, key: string): void => {
96+
clearTimeout(leaveTimer);
97+
previewEmoji.value = key;
98+
99+
const target = event.currentTarget as HTMLElement | null;
100+
const popup = emojiPopupRef.value;
101+
102+
if (target && popup) {
103+
const targetRect = target.getBoundingClientRect();
104+
const popupRect = popup.getBoundingClientRect();
105+
106+
previewStyle.value = {
107+
left: `${targetRect.left - popupRect.left + targetRect.width / 2}px`,
108+
top: `${targetRect.top - popupRect.top}px`,
109+
transform: 'translate(-50%, -100%)',
110+
};
111+
}
112+
};
113+
114+
const onEmojiLeave = (): void => {
115+
leaveTimer = setTimeout(() => {
116+
previewEmoji.value = '';
117+
}, 50);
118+
};
119+
91120
const showGif = ref(false);
92121
const showPreview = ref(false);
93122
const previewText = ref('');
@@ -746,13 +775,19 @@ onMounted(() => {
746775
v-for="(emojiItem, index) in emoji.tabs"
747776
:key="emojiItem.name"
748777
>
749-
<div v-if="index === emojiTabIndex" class="wl-tab-wrapper">
778+
<div
779+
v-if="index === emojiTabIndex"
780+
class="wl-tab-wrapper"
781+
@scroll="onEmojiLeave"
782+
>
750783
<button
751784
v-for="key in emojiItem.items"
752785
:key="key"
753786
type="button"
754787
:title="key"
755788
@click="insert(`:${key}:`)"
789+
@mouseenter="onEmojiHover($event, key)"
790+
@mouseleave="onEmojiLeave"
756791
>
757792
<img
758793
v-if="showEmoji"
@@ -766,6 +801,18 @@ onMounted(() => {
766801
</div>
767802
</template>
768803

804+
<div>
805+
<img
806+
v-if="previewEmoji"
807+
class="wl-emoji-preview"
808+
:src="emoji.map[previewEmoji]"
809+
alt="preview"
810+
loading="lazy"
811+
referrerPolicy="no-referrer"
812+
:style="previewStyle"
813+
/>
814+
</div>
815+
769816
<div v-if="emoji.tabs.length > 1" class="wl-tabs">
770817
<button
771818
v-for="(emojiItem, index) in emoji.tabs"

packages/client/src/styles/emoji.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@
4747
max-height: 1.5em;
4848
}
4949

50+
.wl-emoji-preview {
51+
position: absolute;
52+
top: -4em;
53+
left: 0;
54+
z-index: 1;
55+
56+
display: block;
57+
max-width: 3em;
58+
max-height: 3em;
59+
padding: 0.25em;
60+
border: var(--waline-border);
61+
border-radius: 4px;
62+
63+
background: var(--waline-bg-color);
64+
box-shadow: var(--waline-box-shadow);
65+
}
66+
5067
.wl-tab-wrapper {
5168
overflow-y: auto;
5269
max-height: 145px;

0 commit comments

Comments
 (0)