Skip to content

Commit 98b084f

Browse files
committed
feat(kit): improve tooltip
1 parent 0070073 commit 98b084f

File tree

7 files changed

+180
-31
lines changed

7 files changed

+180
-31
lines changed

packages/core/src/client/webcomponents/.generated/css.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/core/src/client/webcomponents/components/DockEmbedded.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { DevToolsDockEntry } from '@vitejs/devtools-kit'
33
import type { DevToolsDockState } from './DockProps'
44
import Dock from './Dock.vue'
55
import DockPanel from './DockPanel.vue'
6+
import FloatingTooltip from './FloatingTooltip.vue'
67
78
defineProps<{
89
state: DevToolsDockState
@@ -24,4 +25,5 @@ defineProps<{
2425
/>
2526
</template>
2627
</Dock>
28+
<FloatingTooltip />
2729
</template>
Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import type { DevToolsDockEntry } from '@vitejs/devtools-kit'
33
import { toRefs } from 'vue'
4-
import DockIcon from './DockIcon.vue'
4+
import DockEntry from './DockEntry.vue'
55
66
const props = defineProps<{
77
selected?: DevToolsDockEntry
@@ -27,25 +27,14 @@ function toggleDockEntry(dock: DevToolsDockEntry) {
2727
<div
2828
class="vite-devtools-dock-entries flex items-center w-full h-full justify-center transition-opacity duration-300"
2929
>
30-
<div
30+
<DockEntry
3131
v-for="dock of entries"
3232
:key="dock.id"
33-
class="relative group vite-devtools-dock-entry"
34-
>
35-
<button
36-
:title="dock.title"
37-
:class="[
38-
isVertical ? 'rotate-270' : '',
39-
selected ? selected.id !== dock.id ? 'op50 saturate-0' : 'scale-120 text-purple' : '',
40-
]"
41-
class="flex items-center justify-center p1.5 rounded-xl hover:bg-[#8881] hover:scale-120 transition-all duration-300 relative"
42-
@click="toggleDockEntry(dock)"
43-
>
44-
<DockIcon :icon="dock.icon" :title="dock.title" class="w-5 h-5 select-none" />
45-
</button>
46-
<div class="vite-devtools-dock-label text-xs group-hover:opacity-100 opacity-0 transition-opacity duration-300 w-max bg-glass border border-base z-10 rounded px2 absolute p1">
47-
{{ dock.title }}
48-
</div>
49-
</div>
33+
:dock
34+
:is-selected="selected?.id === dock.id"
35+
:is-dimmed="selected && (selected.id !== dock.id)"
36+
:is-vertical="isVertical"
37+
@click="toggleDockEntry(dock)"
38+
/>
5039
</div>
5140
</template>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script setup lang="ts">
2+
import type { DevToolsDockEntry } from '@vitejs/devtools-kit'
3+
import { useEventListener } from '@vueuse/core'
4+
import { useTemplateRef } from 'vue'
5+
import { setFloatingTooltip } from '../state/floating-tooltip'
6+
import DockIcon from './DockIcon.vue'
7+
8+
const props = defineProps<{
9+
dock: DevToolsDockEntry
10+
isSelected?: boolean
11+
isDimmed?: boolean
12+
isVertical?: boolean
13+
}>()
14+
15+
const button = useTemplateRef<HTMLButtonElement>('button')
16+
17+
function updatePos() {
18+
const rect = button.value?.getBoundingClientRect()
19+
if (rect) {
20+
setFloatingTooltip({
21+
text: props.dock.title,
22+
width: rect.width,
23+
height: rect.height,
24+
left: rect.left,
25+
top: rect.top,
26+
})
27+
}
28+
}
29+
30+
function showTitle() {
31+
updatePos()
32+
}
33+
34+
function clearTitle() {
35+
setFloatingTooltip(null)
36+
}
37+
38+
useEventListener('pointerdown', () => {
39+
setFloatingTooltip(null)
40+
})
41+
</script>
42+
43+
<template>
44+
<div
45+
:key="dock.id"
46+
class="relative group vite-devtools-dock-entry"
47+
@pointerenter="showTitle"
48+
@pointerleave="clearTitle"
49+
>
50+
<button
51+
ref="button"
52+
:title="dock.title"
53+
:class="[
54+
isVertical ? 'rotate-270' : '',
55+
isDimmed ? 'op50 saturate-0' : '',
56+
isSelected ? 'scale-120 text-purple' : '',
57+
]"
58+
class="flex items-center justify-center p1.5 rounded-xl hover:bg-[#8881] hover:scale-120 transition-all duration-300 relative"
59+
>
60+
<DockIcon :icon="dock.icon" :title="dock.title" class="w-5 h-5 select-none" />
61+
</button>
62+
</div>
63+
</template>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<script setup lang="ts">
2+
import type { FloatingTooltip } from '../state/floating-tooltip'
3+
import { computed, ref, watchEffect } from 'vue'
4+
import { useFloatingTooltip } from '../state/floating-tooltip'
5+
6+
const DETECT_MARGIN = 100
7+
const GAP = 10
8+
9+
const current = useFloatingTooltip()
10+
const box = ref<FloatingTooltip>({
11+
text: '',
12+
width: 0,
13+
height: 0,
14+
left: 0,
15+
top: 0,
16+
})
17+
18+
// guess alignment of the tooltip based on viewport position
19+
const align = computed<'bottom' | 'left' | 'right' | 'top'>(() => {
20+
const vw = window.innerWidth
21+
const vh = window.innerHeight
22+
if (box.value.left < DETECT_MARGIN)
23+
return 'right'
24+
if (box.value.left + box.value.width > vw - DETECT_MARGIN)
25+
return 'left'
26+
if (box.value.top < DETECT_MARGIN)
27+
return 'bottom'
28+
if (box.value.top + box.value.height > vh - DETECT_MARGIN)
29+
return 'top'
30+
return 'bottom'
31+
})
32+
33+
const style = computed(() => {
34+
switch (align.value) {
35+
case 'bottom': {
36+
return {
37+
left: `${box.value.left + box.value.width / 2}px`,
38+
top: `${box.value.top + box.value.height + GAP}px`,
39+
transform: 'translateX(-50%)',
40+
}
41+
}
42+
case 'top': {
43+
return {
44+
left: `${box.value.left + box.value.width / 2}px`,
45+
bottom: `${window.innerHeight - box.value.top + GAP}px`,
46+
transform: 'translateX(-50%)',
47+
}
48+
}
49+
case 'left': {
50+
return {
51+
right: `${window.innerWidth - box.value.left + GAP}px`,
52+
top: `${box.value.top + box.value.height / 2}px`,
53+
transform: 'translateY(-50%)',
54+
}
55+
}
56+
case 'right': {
57+
return {
58+
left: `${box.value.left + box.value.width + GAP}px`,
59+
top: `${box.value.top + box.value.height / 2}px`,
60+
transform: 'translateY(-50%)',
61+
}
62+
}
63+
default: {
64+
throw new Error('Unreachable')
65+
}
66+
}
67+
})
68+
69+
watchEffect(() => {
70+
if (current.value) {
71+
box.value = { ...current.value }
72+
}
73+
})
74+
</script>
75+
76+
<template>
77+
<div
78+
v-if="box.text"
79+
class="vite-devtools-floating-title z-[2147483645] text-xs transition-all duration-300 w-max bg-glass border border-base rounded px2 fixed p1"
80+
:class="current ? 'op100' : 'op0 pointer-events-none'"
81+
:style="style"
82+
>
83+
{{ box.text }}
84+
</div>
85+
</template>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { shallowRef } from 'vue'
2+
3+
export interface FloatingTooltip {
4+
left: number
5+
top: number
6+
width: number
7+
height: number
8+
text: string
9+
}
10+
11+
const state = shallowRef<FloatingTooltip | null>(null)
12+
13+
export function setFloatingTooltip(info: FloatingTooltip | null) {
14+
state.value = info
15+
}
16+
17+
export function useFloatingTooltip() {
18+
return state
19+
}

packages/core/src/client/webcomponents/style.css

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
--uno: absolute w-[14px] h-[14px] -m-[6px] rounded-md;
1313
}
1414
.vite-devtools-resize-handle {
15-
--uno: z-[2147483645];
15+
--uno: z-[2147483644];
1616
}
1717
.vite-devtools-resize-handle:hover {
1818
--uno: bg-gray-400:10;
1919
}
2020

2121
#vite-devtools-anchor {
22-
--uno: w-0 z-[2147483645] fixed origin-center font-sans text-[15px] box-border;
22+
--uno: w-0 z-[2147483644] fixed origin-center font-sans text-[15px] box-border;
2323
transform: translate(-50%, -50%) rotate(0);
2424
}
2525

@@ -63,12 +63,3 @@
6363
--uno: hidden;
6464
}
6565
}
66-
67-
.vite-devtools-dock-label {
68-
--uno: translate-x--1/2 bottom--7 left-1/2;
69-
}
70-
71-
.vite-devtools-vertical .vite-devtools-dock-label {
72-
--uno: rotate-270 bottom--10;
73-
}
74-

0 commit comments

Comments
 (0)