style: 搜尋說明與列表

This commit is contained in:
2026-06-03 16:06:14 +08:00
parent f54dfda5f4
commit c96e4b1a2f
2 changed files with 132 additions and 44 deletions
+122 -36
View File
@@ -40,7 +40,7 @@
z-index: 5; z-index: 5;
} }
/* 巡迴搜尋:樣式對齊 .search-input;第一列 input+btn,第二列白字結果 */ /* 巡迴搜尋:輸入列 + 奶油說明/結果卡(對齊 #stage-now 白黃風格) */
.concert-search-box { .concert-search-box {
width: 100%; width: 100%;
max-width: 920px; max-width: 920px;
@@ -48,7 +48,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 14px; gap: 16px;
} }
.concert-search-row { .concert-search-row {
@@ -97,83 +97,156 @@
box-shadow: 0 0 10px rgba(255, 204, 0, 0.35); box-shadow: 0 0 10px rgba(255, 204, 0, 0.35);
} }
.concert-search-panel {
width: 100%;
max-width: 720px;
background: #fff8df;
border: 1px solid #e9d99d;
border-radius: 16px;
padding: 18px 20px 16px;
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.12);
}
.concert-search-guide-title {
margin: 0 0 14px;
text-align: center;
font-size: 0.95rem;
font-weight: 700;
letter-spacing: 0.12em;
color: #4a4a4a;
}
.concert-search-guide-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 12px;
}
.concert-search-guide-item {
background: #fff;
border: 1px solid #eadfb2;
border-radius: 12px;
padding: 12px 14px;
}
.concert-search-guide-item .guide-label {
display: block;
margin: 0 0 6px;
text-align: center;
font-size: 0.88rem;
font-weight: 700;
color: #65510e;
}
.concert-search-guide-item .guide-desc {
margin: 0;
text-align: left;
font-size: 0.84rem;
line-height: 1.65;
color: #5a5a5a;
}
.concert-search-guide-item .guide-desc em {
font-style: normal;
color: #7f6712;
font-weight: 600;
}
.concert-search-guide-steps {
margin: 14px 0 0;
padding-top: 12px;
border-top: 1px dashed #e0d4a8;
text-align: center;
font-size: 0.82rem;
line-height: 1.6;
color: #6a6a6a;
}
.concert-search-guide-steps strong {
color: #4a4a4a;
font-weight: 700;
}
.concert-search-result { .concert-search-result {
display: block; display: block;
width: 100%; width: 100%;
margin: 0; margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid #eadfb2;
} }
.concert-search-hint { .concert-search-result:not(:has(.concert-search-hits)) .concert-search-summary {
width: 100%; margin: 0;
max-width: 720px;
margin: 0 0 12px;
padding: 0 6px;
text-align: left;
font-size: 0.82rem;
line-height: 1.6;
color: rgba(255, 255, 255, 0.78);
} }
.concert-search-summary { .concert-search-summary {
margin: 0 0 12px; margin: 0 0 12px;
text-align: center; text-align: center;
font-size: 0.95rem; font-size: 0.92rem;
line-height: 1.6; line-height: 1.65;
color: #fff; color: #444;
} }
.concert-search-hits { .concert-search-hits {
list-style: none; list-style: none;
margin: 0 auto; margin: 0;
padding: 0; padding: 0;
max-width: 720px; display: grid;
gap: 10px;
text-align: left; text-align: left;
} }
.concert-search-hits li { .concert-search-hits li {
padding: 10px 14px; padding: 12px 14px;
margin-bottom: 8px; background: #fff;
background: rgba(255, 248, 223, 0.1); border: 1px solid #eadfb2;
border: 1px solid rgba(255, 204, 0, 0.35);
border-radius: 12px; border-radius: 12px;
color: #fff; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.05);
font-size: 0.88rem; font-size: 0.88rem;
line-height: 1.5; line-height: 1.5;
} }
.concert-search-hits li.concert-search-hit-jump { .concert-search-hits li.concert-search-hit-jump {
cursor: pointer; cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease; transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
} }
.concert-search-hits li.concert-search-hit-jump:hover, .concert-search-hits li.concert-search-hit-jump:hover,
.concert-search-hits li.concert-search-hit-jump:focus-visible { .concert-search-hits li.concert-search-hit-jump:focus-visible,
background: rgba(255, 204, 0, 0.15); .concert-search-hits li.concert-search-hit-jump.is-active {
border-color: #ffcc00; border-color: #ffcc00;
box-shadow: 0 6px 18px rgba(255, 204, 0, 0.22);
transform: translateY(-1px);
outline: none; outline: none;
} }
.concert-search-hits .hit-track { .concert-search-hits .hit-track {
display: block; display: block;
font-weight: 700; font-weight: 700;
color: #ffdd00; color: #2c2c2c;
} }
.concert-search-hits .hit-meta { .concert-search-hits .hit-meta {
display: block; display: block;
margin-top: 4px; margin-top: 6px;
color: rgba(255, 255, 255, 0.78); color: #6a6a6a;
font-size: 0.82rem; font-size: 0.8rem;
line-height: 1.5;
} }
.concert-search-hits .hit-more { .concert-search-hits .hit-more {
text-align: center; text-align: center;
border-style: dashed; border-style: dashed;
color: rgba(255, 255, 255, 0.85); background: #fffef8;
color: #7f6712;
cursor: default;
box-shadow: none;
} }
.track-item.is-search-hit .track { .track-item.is-search-hit .track {
color: #ffdd00; color: #c9a100;
font-weight: 700; font-weight: 700;
} }
</style> </style>
@@ -200,11 +273,24 @@
placeholder="成員或 ENCORESolo…(可空白)" aria-describedby="concert-search-hint"> placeholder="成員或 ENCORESolo…(可空白)" aria-describedby="concert-search-hint">
<button type="button" class="concert-search-btn" id="concert-search-btn">查詢</button> <button type="button" class="concert-search-btn" id="concert-search-btn">查詢</button>
</div> </div>
<p class="concert-search-hint" id="concert-search-hint"> <div class="concert-search-panel">
<strong>曲目或場次</strong>:曲名、巡演名或場地(例:COUPLE、釜山、YELLOW NOTE)。<strong>成員或段落</strong>:可留白;要縮小範圍時可填成員名,或歌單裡的段落標記(例:殷志源、ENCORE、BLACKKIES、Solo)。兩格都有填時,需同時符合。按查詢或 Enter;點結果可跳到下方歌單。 <section class="concert-search-guide" id="concert-search-hint" aria-label="搜尋說明">
</p> <h2 class="concert-search-guide-title">怎麼搜尋</h2>
<div id="concert-search-result" class="concert-search-result" aria-live="polite"> <ul class="concert-search-guide-list">
<p class="concert-search-summary">尚未查詢。</p> <li class="concert-search-guide-item">
<span class="guide-label">曲目或場次</span>
<p class="guide-desc">比對曲名、巡演名或場地。例:<em>COUPLE</em><em>釜山</em><em>YELLOW NOTE</em></p>
</li>
<li class="concert-search-guide-item">
<span class="guide-label">成員或段落</span>
<p class="guide-desc">可留白。要縮小範圍可填成員名,或歌單標記(安可、小分隊、Solo)。例:<em>殷志源</em><em>ENCORE</em><em>BLACKKIES</em></p>
</li>
</ul>
<p class="concert-search-guide-steps"><strong></strong> 查詢或 Enter 顯示結果列表 <strong></strong> 點列表某一列,跳到下方該場次與曲目(兩格都有填時須同時符合)</p>
</section>
<div id="concert-search-result" class="concert-search-result" aria-live="polite">
<p class="concert-search-summary">尚未查詢。</p>
</div>
</div> </div>
</div> </div>
</div> </div>
+10 -8
View File
@@ -52,8 +52,10 @@ function expandVenueForTrackItem(trackItem) {
} }
function scrollToSearchResults() { function scrollToSearchResults() {
if (!myResult) return; const panel = document.querySelector(".concert-search-panel");
myResult.scrollIntoView({ behavior: "smooth", block: "start" }); const target = panel || myResult;
if (!target) return;
target.scrollIntoView({ behavior: "smooth", block: "start" });
} }
function jumpToTrackHit(hit) { function jumpToTrackHit(hit) {
@@ -84,21 +86,20 @@ function renderSearchResults(getQuery, getMember, hits) {
if (!hits.length) { if (!hits.length) {
summary.textContent = `曲目或場次「${getQuery || "(未填)"}${getMember.trim() ? `、成員「${getMember}` : ""}:沒有比對到曲目,請換字再試。`; summary.textContent = `曲目或場次「${getQuery || "(未填)"}${getMember.trim() ? `、成員「${getMember}` : ""}:沒有比對到曲目,請換字再試。`;
myResult.replaceChildren(summary); myResult.replaceChildren(summary);
scrollToSearchResults();
return; return;
} }
if (getMember.trim()) { if (getMember.trim()) {
summary.textContent = `曲目或場次「${getQuery || "(未填)"}」、成員「${getMember}」:共 ${hits.length} 筆。點任一列可跳到歌單。`; summary.textContent = `曲目或場次「${getQuery || "(未填)"}」、成員「${getMember}」:共 ${hits.length} 筆。請點列表中的場次,再跳到下方歌單。`;
} else { } else {
summary.textContent = `曲目或場次「${getQuery}」:共 ${hits.length} 筆。點任一列可跳到歌單。`; summary.textContent = `曲目或場次「${getQuery}」:共 ${hits.length} 筆。請點列表中的場次,再跳到下方歌單。`;
} }
const list = document.createElement("ul"); const list = document.createElement("ul");
list.className = "concert-search-hits"; list.className = "concert-search-hits";
hits.slice(0, SEARCH_LIST_MAX).forEach(function (hit) { hits.slice(0, SEARCH_LIST_MAX).forEach(function (hit) {
if (hit.el) hit.el.classList.add("is-search-hit");
const li = document.createElement("li"); const li = document.createElement("li");
li.className = "concert-search-hit-jump"; li.className = "concert-search-hit-jump";
li.setAttribute("role", "button"); li.setAttribute("role", "button");
@@ -117,11 +118,13 @@ function renderSearchResults(getQuery, getMember, hits) {
if (metaLine.textContent) li.appendChild(metaLine); if (metaLine.textContent) li.appendChild(metaLine);
li.addEventListener("click", function () { li.addEventListener("click", function () {
setActiveSearchListItem(list, li);
jumpToTrackHit(hit); jumpToTrackHit(hit);
}); });
li.addEventListener("keydown", function (event) { li.addEventListener("keydown", function (event) {
if (event.key === "Enter" || event.key === " ") { if (event.key === "Enter" || event.key === " ") {
event.preventDefault(); event.preventDefault();
setActiveSearchListItem(list, li);
jumpToTrackHit(hit); jumpToTrackHit(hit);
} }
}); });
@@ -137,8 +140,7 @@ function renderSearchResults(getQuery, getMember, hits) {
} }
myResult.replaceChildren(summary, list); myResult.replaceChildren(summary, list);
scrollToSearchResults();
jumpToTrackHit(hits[0]);
} }
function runSearch() { function runSearch() {