97 lines
3.3 KiB
Vue
97 lines
3.3 KiB
Vue
<script setup lang="ts">
|
|
import tinycolor from "tinycolor2";
|
|
import type { VNodeRef } from "vue";
|
|
import Badge from "../badge/Badge.vue";
|
|
import { Dot } from "lucide-vue-next";
|
|
|
|
const imageElement = shallowRef<VNodeRef | null>(null);
|
|
|
|
const palette = useVibrantPalette(imageElement);
|
|
const paletteVibrantHsl = ref<string>('');
|
|
|
|
watch(palette, () => {
|
|
if (!palette.value?.Vibrant) return;
|
|
const [hue, saturation, lightness] = palette.value.Vibrant.hsl;
|
|
const lighterColor = tinycolor({ h: hue * 360, s: saturation, l: lightness }).brighten().desaturate().toHslString()
|
|
paletteVibrantHsl.value = lighterColor;
|
|
});
|
|
|
|
interface Props {
|
|
image_url: string;
|
|
image_alt?: string;
|
|
title: string;
|
|
type: 'anime' | 'manga' | 'character' | 'voice_actor';
|
|
|
|
subtitle?: string;
|
|
description?: string;
|
|
|
|
tooltipEnabled?: boolean;
|
|
tooltipMetadata?: {
|
|
studio: string;
|
|
type: string;
|
|
episodes: number;
|
|
rating: number;
|
|
|
|
tags?: string[];
|
|
};
|
|
tooltipCollisionBoundary?: Element;
|
|
|
|
dynamicColor?: boolean;
|
|
|
|
onClick?: () => void;
|
|
onHover?: (isHovering: boolean) => void;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
</script>
|
|
|
|
<template>
|
|
<div class="h-full overflow-hidden max-w-64">
|
|
<div class="flex flex-col gap-2 cursor-pointer text-muted-foreground vibrant-color">
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger as-child>
|
|
<NuxtImg class="rounded-lg" ref="imageElement" :src="props.image_url" sizes="128px md:256px"
|
|
quality="100" />
|
|
<p class="text-lg line-clamp-2 font-semibold">
|
|
{{ props.title }}
|
|
</p>
|
|
</TooltipTrigger>
|
|
<TooltipContent :sideOffset="16" :collisionBoundary="props.tooltipCollisionBoundary"
|
|
:avoidCollisions="true" side="right" class="bg-muted text-muted-foreground"
|
|
arrowClass="bg-muted fill-muted">
|
|
<div class="flex flex-col gap-1 m-4">
|
|
<div class="flex">
|
|
<p class="text-xl font-bold truncate max-w-[24ch]">{{ props.title }}</p>
|
|
</div>
|
|
<p class="text-lg font-semibold tracking-wider" :style="{ color: paletteVibrantHsl }">{{
|
|
props.tooltipMetadata?.studio }}
|
|
</p>
|
|
<div class="flex items-center text-base font-semibold">
|
|
<p>{{ props.tooltipMetadata?.type }}</p>
|
|
<Dot />
|
|
<p>{{ props.tooltipMetadata?.rating }} episodes</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<Badge v-for="tag in props.tooltipMetadata?.tags">
|
|
{{ tag }}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.vibrant-color:hover {
|
|
color: v-bind(paletteVibrantHsl);
|
|
}
|
|
|
|
.vibrant-color {
|
|
transition: color ease-in 0.1s;
|
|
}
|
|
</style>
|