|
2 | 2 | let hoveredRow = $state<string | null>(null); |
3 | 3 | import * as m from '$msg'; |
4 | 4 | import type { ModRepoMetadata, ModMetadata } from 'mclib'; |
| 5 | + import { slide } from 'svelte/transition'; |
5 | 6 |
|
6 | 7 | let { |
7 | 8 | search_results = $bindable(), |
|
28 | 29 | // If no scale applies, just return the number as a string |
29 | 30 | return input.toString(); |
30 | 31 | } |
| 32 | +
|
| 33 | + $effect(() => { |
| 34 | + const scrollableList = document.querySelectorAll('div.scrollable'); |
| 35 | + if (scrollableList) { |
| 36 | + for (const scrollableDiv of scrollableList.values()) { |
| 37 | + if (scrollableDiv.id == hoveredRow) |
| 38 | + scrollableDiv.querySelector('.links')?.scrollIntoView({ block: 'nearest' }); |
| 39 | + else scrollableDiv.querySelector('.displayed')?.scrollIntoView({ block: 'nearest' }); |
| 40 | + } |
| 41 | + } |
| 42 | + }); |
31 | 43 | </script> |
32 | 44 |
|
33 | | -<table id="mod_search_list"> |
34 | | - <!-- <thead> |
35 | | - <tr> |
36 | | - <th>Icon</th> |
37 | | - <th>Name</th> |
38 | | - <th>Downloads</th> |
39 | | - <th>Link</th> |
40 | | - </tr> |
41 | | - </thead> --> |
42 | | - <tbody> |
43 | | - {#each search_results.slice(0, 10) as meta (meta[0].id)} |
44 | | - {@const firstRepoMeta = meta[0]} |
45 | | - <tr> |
46 | | - <td |
47 | | - class="img" |
48 | | - onclick={() => { |
49 | | - add_mod_to_list(firstRepoMeta); |
50 | | - }} |
51 | | - > |
52 | | - <img src={firstRepoMeta.imageURL} alt="{firstRepoMeta.name} pic" /> |
53 | | - </td> |
54 | | - <td |
55 | | - onclick={() => { |
56 | | - add_mod_to_list(firstRepoMeta); |
57 | | - }} |
58 | | - > |
59 | | - {firstRepoMeta.name} |
60 | | - </td> |
61 | | - <td |
62 | | - onclick={() => { |
63 | | - add_mod_to_list(firstRepoMeta); |
64 | | - }} |
65 | | - > |
66 | | - {humanize_number(firstRepoMeta.downloadCount) + |
67 | | - ' ' + |
68 | | - m['add_mods.search_results.downloads_count']()} |
69 | | - </td> |
70 | | - <td class="link" style="position:relative;"> |
71 | | - <a |
72 | | - href={firstRepoMeta.homepageURL} |
73 | | - target="_blank" |
74 | | - rel="noopener noreferrer" |
| 45 | +<ul id="mod_search_list"> |
| 46 | + {#each search_results.slice(0, 10) as modMeta (modMeta[0].id)} |
| 47 | + {@const firstRepoMeta = modMeta[0]} |
| 48 | + <li class="scroller"> |
| 49 | + <div |
| 50 | + role="button" |
| 51 | + tabindex="0" |
| 52 | + class="scrollable" |
| 53 | + id={firstRepoMeta.id} |
| 54 | + onmouseleave={() => { |
| 55 | + hoveredRow = null; |
| 56 | + }} |
| 57 | + > |
| 58 | + <div class="displayed"> |
| 59 | + <div |
| 60 | + class="infos" |
| 61 | + role="button" |
| 62 | + tabindex={0} |
| 63 | + onclick={() => { |
| 64 | + add_mod_to_list(firstRepoMeta); |
| 65 | + }} |
| 66 | + onkeydown={(ke) => { |
| 67 | + if (ke.code == 'Enter') add_mod_to_list(firstRepoMeta); |
| 68 | + }} |
| 69 | + > |
| 70 | + <div class="name"> |
| 71 | + <img src={firstRepoMeta.imageURL} alt="{firstRepoMeta.name} pic" /> |
| 72 | + <p> |
| 73 | + {firstRepoMeta.name} |
| 74 | + </p> |
| 75 | + </div> |
| 76 | + <p> |
| 77 | + {humanize_number(modMeta.reduce((sum, current) => sum + current.downloadCount, 0)) + |
| 78 | + ' ' + |
| 79 | + m['add_mods.search_results.downloads_count']()} |
| 80 | + </p> |
| 81 | + </div> |
| 82 | + <div |
| 83 | + role="button" |
| 84 | + tabindex={0} |
| 85 | + class="see-links {hoveredRow == firstRepoMeta.id ? 'hide' : ''}" |
75 | 86 | onmouseenter={() => { |
76 | 87 | hoveredRow = firstRepoMeta.id; |
77 | 88 | }} |
78 | 89 | onmouseleave={() => { |
79 | 90 | hoveredRow = null; |
80 | 91 | }} |
81 | 92 | > |
82 | | - {m['add_mods.search_results.open_mod_repo_link']({ |
83 | | - repo_name: firstRepoMeta.repository |
84 | | - })} |
85 | | - {meta.length > 1 ? ' and more' : ''} |
86 | | - </a> |
87 | | - {#if meta.length > 1 && hoveredRow === firstRepoMeta.id} |
88 | | - <span class="repo-tooltip"> |
89 | | - Found on: {meta.map((m) => m.repository).join(', ')} |
90 | | - </span> |
91 | | - {/if} |
92 | | - </td> |
93 | | - </tr> |
94 | | - {/each} |
95 | | - </tbody> |
96 | | -</table> |
| 93 | + <p>{m['add_mods.search_results.repo_from']()} »</p> |
| 94 | + </div> |
| 95 | + </div> |
| 96 | + <div |
| 97 | + class="links" |
| 98 | + role="list" |
| 99 | + onmouseenter={() => { |
| 100 | + hoveredRow = firstRepoMeta.id; |
| 101 | + }} |
| 102 | + onmouseleave={() => { |
| 103 | + hoveredRow = null; |
| 104 | + }} |
| 105 | + > |
| 106 | + {#each modMeta as repo (repo.id)} |
| 107 | + <a href={repo.homepageURL} target="_blank" rel="noopener noreferrer" |
| 108 | + >{repo.repository.toUpperCase()}</a |
| 109 | + > |
| 110 | + {/each} |
| 111 | + </div> |
| 112 | + </div> |
| 113 | + </li> |
| 114 | + {/each} |
| 115 | +</ul> |
97 | 116 |
|
98 | 117 | <style> |
99 | | - .repo-tooltip { |
100 | | - position: absolute; |
101 | | - background: var(--grey-dark-2); |
102 | | - color: var(--text-light); |
103 | | - padding: 0.3rem 0.7rem; |
104 | | - border-radius: 0.3rem; |
105 | | - font-size: 0.85rem; |
106 | | - z-index: 10; |
107 | | - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
108 | | - margin-top: 0.2rem; |
109 | | - right: 0; |
110 | | - white-space: nowrap; |
111 | | - } |
112 | | - table#mod_search_list { |
113 | | - border-spacing: 0 0.5rem; |
114 | | - padding: 0 0.5rem; |
| 118 | + ul#mod_search_list { |
| 119 | + padding: 0.5rem 0.5rem; |
115 | 120 | background: var(--grey-dark-1); |
116 | 121 | font-size: 0.9rem; |
117 | 122 | width: 100%; |
118 | | - & tbody tr { |
119 | | - &:is(:focus, :focus-visible, :active, :hover) td { |
120 | | - background: var(--grey); |
121 | | - } |
122 | | - & td { |
123 | | - &.img { |
124 | | - padding: 0.4rem; |
125 | | - padding-bottom: 0.1rem; |
126 | | - width: 32px; |
127 | | - height: 32px; |
128 | | - & img { |
129 | | - width: 32px; |
130 | | - height: 32px; |
| 123 | + display: flex; |
| 124 | + flex-direction: column; |
| 125 | + & li.scroller { |
| 126 | + width: 100%; |
| 127 | + overflow-x: hidden; |
| 128 | + scroll-snap-type: x mandatory; |
| 129 | + scroll-behavior: smooth; |
| 130 | + & .scrollable { |
| 131 | + display: flex; |
| 132 | + flex-direction: row; |
| 133 | + align-items: stretch; |
| 134 | + width: 200%; |
| 135 | + & .displayed { |
| 136 | + display: flex; |
| 137 | + flex-direction: row; |
| 138 | + align-items: stretch; |
| 139 | + width: 50%; |
| 140 | + & div.infos { |
| 141 | + padding: 0.4rem; |
| 142 | + flex: 1; |
| 143 | + display: flex; |
| 144 | + flex-direction: row; |
| 145 | + justify-content: space-between; |
| 146 | + gap: 0.5rem; |
| 147 | + align-items: center; |
| 148 | + cursor: pointer; |
| 149 | + z-index: 1; |
| 150 | + background: var(--grey-dark-2); |
| 151 | + & div.name { |
| 152 | + display: flex; |
| 153 | + flex-direction: row; |
| 154 | + align-items: center; |
| 155 | + gap: 0.5rem; |
| 156 | + } |
| 157 | + &:hover { |
| 158 | + background: var(--grey); |
| 159 | + } |
| 160 | + & img { |
| 161 | + width: 32px; |
| 162 | + height: 32px; |
| 163 | + } |
| 164 | + } |
| 165 | + & div.see-links { |
| 166 | + padding: 0.5rem; |
| 167 | + align-content: center; |
| 168 | + background: var(--blue); |
| 169 | + white-space: nowrap; |
| 170 | + overflow: hidden; |
131 | 171 | } |
132 | 172 | } |
133 | | - &.link { |
134 | | - text-align: end; |
| 173 | + & .links { |
| 174 | + display: flex; |
| 175 | + flex-direction: row; |
| 176 | + align-items: stretch; |
| 177 | + & a { |
| 178 | + align-content: center; |
| 179 | + padding: 0.7rem; |
| 180 | + background: var(--blue-dark-1); |
| 181 | + color: var(--grey-light-1); |
| 182 | + text-decoration: none; |
| 183 | + &:is(:focus, :focus-visible, :active, :hover) { |
| 184 | + background: var(--blue-light-1); |
| 185 | + color: var(--grey-dark-2); |
| 186 | + } |
| 187 | + } |
135 | 188 | } |
136 | | - cursor: pointer; |
137 | | - padding: 0.4rem; |
138 | | - background: var(--grey-dark-2); |
139 | 189 | } |
140 | 190 | } |
141 | 191 | } |
|
0 commit comments