frontend: add item tooltip, refactor with itemicon component
This commit is contained in:
137
frontend/components/item/ItemIcon.vue
Normal file
137
frontend/components/item/ItemIcon.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<script setup lang="ts">
|
||||
import { CDRAGON_BASE, mapPath } from '~/utils/cdragon'
|
||||
|
||||
interface Props {
|
||||
item: Item
|
||||
size?: number
|
||||
showPickrate?: boolean
|
||||
pickrate?: number
|
||||
class?: string
|
||||
}
|
||||
|
||||
// Expose the icon element for external use (e.g., arrow drawing)
|
||||
const iconElement = ref<HTMLElement | null>(null)
|
||||
|
||||
defineExpose({
|
||||
iconElement
|
||||
})
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: 48,
|
||||
showPickrate: false,
|
||||
pickrate: 0,
|
||||
class: ''
|
||||
})
|
||||
|
||||
// Tooltip state - encapsulated in this component
|
||||
const tooltipState = reactive({
|
||||
show: false,
|
||||
item: null as Item | null,
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
|
||||
const handleMouseEnter = (event: MouseEvent) => {
|
||||
tooltipState.item = props.item
|
||||
|
||||
// Calculate optimal position to keep tooltip within viewport
|
||||
// Don't estimate height - position based on cursor location
|
||||
const tooltipWidth = 300 // Maximum width from CSS
|
||||
const padding = 10 // Minimum padding from edges
|
||||
const offset = 15 // Distance from cursor
|
||||
|
||||
// Get viewport dimensions
|
||||
const viewportWidth = window.innerWidth
|
||||
const viewportHeight = window.innerHeight
|
||||
|
||||
// Right edge detection: if we're in the right half, position to the left
|
||||
let x = event.clientX + offset
|
||||
if (event.clientX + tooltipWidth + offset > viewportWidth - padding) {
|
||||
x = event.clientX - tooltipWidth - offset
|
||||
// Clamp if still off-screen
|
||||
if (x < padding) {
|
||||
x = padding
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom edge detection: if we're in the bottom half, position above
|
||||
// Use a smaller offset for vertical to keep it close
|
||||
let y = event.clientY + offset
|
||||
if (event.clientY > viewportHeight * 0.7) {
|
||||
y = event.clientY - offset - 200 // Position ~200px above
|
||||
// Clamp if too high
|
||||
if (y < padding) {
|
||||
y = padding
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure Y is within reasonable bounds
|
||||
y = Math.min(y, viewportHeight - padding)
|
||||
|
||||
tooltipState.x = x
|
||||
tooltipState.y = y
|
||||
tooltipState.show = true
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
tooltipState.show = false
|
||||
tooltipState.item = null
|
||||
}
|
||||
|
||||
const itemIconPath = computed(() => CDRAGON_BASE + mapPath(props.item.iconPath))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="item-icon-wrapper" @mouseleave="handleMouseLeave">
|
||||
<div
|
||||
ref="iconElement"
|
||||
class="item-icon"
|
||||
:class="props.class"
|
||||
:style="{ width: size + 'px', height: size + 'px' }"
|
||||
@mouseenter="handleMouseEnter"
|
||||
>
|
||||
<NuxtImg :src="itemIconPath" :alt="item.name || 'Item'" class="item-img" />
|
||||
</div>
|
||||
|
||||
<span v-if="showPickrate" class="item-pickrate"> {{ (pickrate * 100).toFixed(0) }}% </span>
|
||||
|
||||
<ItemTooltip
|
||||
:show="tooltipState.show"
|
||||
:item="tooltipState.item"
|
||||
:x="tooltipState.x"
|
||||
:y="tooltipState.y"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item-icon-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--color-on-surface);
|
||||
overflow: hidden;
|
||||
cursor: help;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.item-pickrate {
|
||||
font-size: 0.65rem;
|
||||
color: var(--color-on-surface);
|
||||
opacity: 0.6;
|
||||
margin-top: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user