Files
buildpath/frontend/components/item/ItemIcon.vue

138 lines
3.1 KiB
Vue

<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>