|
1 | 1 | <script lang="ts"> |
2 | | - import GameHoverPreview from "@/components/UI/GameHoverPreview.svelte"; |
3 | | - import { loadGamePreviewScreenshots } from "@/lib/useGameScreenshots"; |
4 | | - import type { CollectionElement, VndbScreenshot } from "@/lib/types"; |
| 2 | + import type { CollectionElement } from "@/lib/types"; |
| 3 | + import { formatLastPlayed, formatPlayTime } from "@/lib/utils"; |
5 | 4 | import { convertFileSrc } from "@tauri-apps/api/core"; |
6 | 5 | import { link } from "svelte-spa-router"; |
7 | 6 |
|
|
10 | 9 | $: src = `${convertFileSrc(collectionElement.thumbnail)}?v=${ |
11 | 10 | collectionElement.updatedAt |
12 | 11 | }`; |
13 | | -
|
14 | | - let hoverNode: HTMLAnchorElement; |
15 | | - let hoverTimer: ReturnType<typeof setTimeout> | null = null; |
16 | | - let isPreviewVisible = false; |
17 | | - let previewScreenshots: VndbScreenshot[] = []; |
18 | | - let hoverToken = 0; |
19 | | -
|
20 | | - const closePreview = () => { |
21 | | - hoverToken += 1; |
22 | | - isPreviewVisible = false; |
23 | | - if (hoverTimer) { |
24 | | - clearTimeout(hoverTimer); |
25 | | - hoverTimer = null; |
26 | | - } |
27 | | - }; |
28 | | -
|
29 | | - const openPreview = () => { |
30 | | - closePreview(); |
31 | | - const token = hoverToken; |
32 | | - hoverTimer = setTimeout(async () => { |
33 | | - isPreviewVisible = true; |
34 | | - previewScreenshots = []; |
35 | | - const screenshots = await loadGamePreviewScreenshots(collectionElement); |
36 | | - if (token === hoverToken) { |
37 | | - previewScreenshots = screenshots; |
38 | | - } |
39 | | - }, 500); |
40 | | - }; |
41 | 12 | </script> |
42 | 13 |
|
43 | 14 | <div class="w-full h-full relative"> |
44 | 15 | <a |
45 | | - bind:this={hoverNode} |
46 | 16 | href={`/works/${collectionElement.id}?gamename=${collectionElement.gamename}`} |
47 | 17 | use:link |
48 | | - on:mouseenter={openPreview} |
49 | | - on:mouseleave={closePreview} |
50 | | - on:focus={openPreview} |
51 | | - on:blur={closePreview} |
52 | 18 | class="block w-full h-full relative group overflow-hidden rounded-xl shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1" |
53 | 19 | > |
54 | 20 | <div class="w-full h-full bg-bg-secondary"> |
|
81 | 47 | /> |
82 | 48 |
|
83 | 49 | <div |
84 | | - class="absolute bottom-0 left-0 right-0 p-4 translate-y-full group-hover:translate-y-0 transition-transform duration-300" |
| 50 | + class="absolute bottom-0 left-0 right-0 p-4 translate-y-full group-hover:translate-y-0 transition-transform duration-300 flex flex-col gap-1" |
85 | 51 | > |
86 | 52 | <div class="text-white font-bold text-sm line-clamp-2 drop-shadow-md"> |
87 | 53 | {collectionElement.gamename} |
88 | 54 | </div> |
| 55 | + |
| 56 | + <div |
| 57 | + class="flex flex-col gap-0.5 text-xs text-gray-300 font-medium drop-shadow-md opacity-0 group-hover:opacity-100 transition-opacity duration-300 delay-100" |
| 58 | + > |
| 59 | + {#if collectionElement.lastPlayAt} |
| 60 | + <div>最終プレイ: {formatLastPlayed(collectionElement.lastPlayAt)}</div> |
| 61 | + {/if} |
| 62 | + {#if collectionElement.totalPlayTimeSeconds > 0} |
| 63 | + <div> |
| 64 | + プレイ時間: {formatPlayTime(collectionElement.totalPlayTimeSeconds)} |
| 65 | + </div> |
| 66 | + {/if} |
| 67 | + </div> |
89 | 68 | </div> |
90 | 69 |
|
91 | 70 | {#if collectionElement.playStatus === 2} |
|
96 | 75 | </div> |
97 | 76 | {/if} |
98 | 77 | </a> |
99 | | - |
100 | | - {#if isPreviewVisible} |
101 | | - <GameHoverPreview |
102 | | - {collectionElement} |
103 | | - screenshots={previewScreenshots} |
104 | | - anchorElement={hoverNode} |
105 | | - /> |
106 | | - {/if} |
107 | 78 | </div> |
0 commit comments