Files
buildpath/frontend/components/rune/RuneIcon.vue
Valentin Haudiquet 686962b678
All checks were successful
pipeline / lint-and-format (push) Successful in 4m18s
pipeline / build-and-push-images (push) Successful in 1m21s
feat/frontend: add tooltips for runes
2026-04-30 13:35:28 +02:00

135 lines
2.9 KiB
Vue

<script setup lang="ts">
import { CDRAGON_BASE, mapPath } from '~/utils/cdragon'
import type { Perk } from '~/types/cdragon'
interface Props {
perk: Perk
size?: number
isActive?: boolean
isKeystone?: boolean
class?: string
}
const props = withDefaults(defineProps<Props>(), {
size: 48,
isActive: false,
isKeystone: false,
class: ''
})
// Tooltip state - encapsulated in this component
const tooltipState = reactive({
show: false,
perk: null as Perk | null,
x: 0,
y: 0
})
const handleMouseEnter = (event: MouseEvent) => {
tooltipState.perk = props.perk
// Calculate optimal position to keep tooltip within viewport
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
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.perk = null
}
const perkIconPath = computed(() => CDRAGON_BASE + mapPath(props.perk.iconPath))
</script>
<template>
<div class="rune-icon-wrapper" @mouseleave="handleMouseLeave">
<div
class="rune-icon"
:class="[
props.class,
{
'rune-activated': isActive,
'rune-keystone': isKeystone
}
]"
:style="{ width: size + 'px', height: size + 'px' }"
@mouseenter="handleMouseEnter"
>
<NuxtImg :src="perkIconPath" :alt="perk.name || 'Rune'" class="rune-img" />
</div>
<RuneTooltip
:show="tooltipState.show"
:perk="tooltipState.perk"
:x="tooltipState.x"
:y="tooltipState.y"
/>
</div>
</template>
<style scoped>
.rune-icon-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.rune-icon {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
border: 1px solid var(--color-on-surface);
overflow: hidden;
cursor: help;
position: relative;
}
.rune-icon.rune-keystone {
border: none;
}
.rune-img {
width: 100%;
height: 100%;
filter: grayscale(1);
}
.rune-icon.rune-activated .rune-img {
filter: none;
}
</style>