Upload 2 files
Browse files- static/app.js +57 -1
- static/style.css +34 -0
static/app.js
CHANGED
|
@@ -250,6 +250,17 @@ function saveHistory(items) {
|
|
| 250 |
localStorage.setItem(HISTORY_KEY, JSON.stringify(items.slice(0, 20)));
|
| 251 |
}
|
| 252 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
function addHistory(query) {
|
| 254 |
if (!STATE.historyEnabled || !query) return;
|
| 255 |
const items = getHistory().filter((item) => item !== query);
|
|
@@ -263,7 +274,11 @@ function renderHistoryDropdown() {
|
|
| 263 |
DOM.searchHistoryDropdown.style.display = "none";
|
| 264 |
return;
|
| 265 |
}
|
| 266 |
-
DOM.searchHistoryDropdown.innerHTML = items.map((item) => `
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
DOM.searchHistoryDropdown.style.display = "";
|
| 268 |
}
|
| 269 |
|
|
@@ -687,6 +702,17 @@ function attachEvents() {
|
|
| 687 |
DOM.searchInput.addEventListener("focus", renderHistoryDropdown);
|
| 688 |
DOM.searchInput.addEventListener("blur", () => setTimeout(() => { DOM.searchHistoryDropdown.style.display = "none"; }, 100));
|
| 689 |
DOM.searchHistoryDropdown.addEventListener("click", (event) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
const item = event.target.closest("[data-query]");
|
| 691 |
if (!item) return;
|
| 692 |
DOM.searchInput.value = item.dataset.query;
|
|
@@ -694,6 +720,35 @@ function attachEvents() {
|
|
| 694 |
syncUrl();
|
| 695 |
doSearch();
|
| 696 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 697 |
let timer = null;
|
| 698 |
DOM.searchInput.addEventListener("input", () => {
|
| 699 |
clearTimeout(timer);
|
|
@@ -701,6 +756,7 @@ function attachEvents() {
|
|
| 701 |
STATE.query = DOM.searchInput.value.trim();
|
| 702 |
STATE.page = 1;
|
| 703 |
addHistory(STATE.query);
|
|
|
|
| 704 |
syncUrl();
|
| 705 |
doSearch();
|
| 706 |
}, 250);
|
|
|
|
| 250 |
localStorage.setItem(HISTORY_KEY, JSON.stringify(items.slice(0, 20)));
|
| 251 |
}
|
| 252 |
|
| 253 |
+
function removeHistory(query) {
|
| 254 |
+
const items = getHistory().filter((item) => item !== query);
|
| 255 |
+
saveHistory(items);
|
| 256 |
+
renderHistoryDropdown();
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
function clearHistory() {
|
| 260 |
+
saveHistory([]);
|
| 261 |
+
renderHistoryDropdown();
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
function addHistory(query) {
|
| 265 |
if (!STATE.historyEnabled || !query) return;
|
| 266 |
const items = getHistory().filter((item) => item !== query);
|
|
|
|
| 274 |
DOM.searchHistoryDropdown.style.display = "none";
|
| 275 |
return;
|
| 276 |
}
|
| 277 |
+
DOM.searchHistoryDropdown.innerHTML = items.map((item) => `
|
| 278 |
+
<div class="history-item" data-query="${escapeHTML(item)}">
|
| 279 |
+
<span class="history-text">${escapeHTML(item)}</span>
|
| 280 |
+
<button type="button" class="history-del" data-del="${escapeHTML(item)}" aria-label="删除历史">×</button>
|
| 281 |
+
</div>`).join("") + '<div class="history-footer"><button type="button" class="history-clear-all">清空历史</button></div>';
|
| 282 |
DOM.searchHistoryDropdown.style.display = "";
|
| 283 |
}
|
| 284 |
|
|
|
|
| 702 |
DOM.searchInput.addEventListener("focus", renderHistoryDropdown);
|
| 703 |
DOM.searchInput.addEventListener("blur", () => setTimeout(() => { DOM.searchHistoryDropdown.style.display = "none"; }, 100));
|
| 704 |
DOM.searchHistoryDropdown.addEventListener("click", (event) => {
|
| 705 |
+
const delBtn = event.target.closest(".history-del");
|
| 706 |
+
if (delBtn) {
|
| 707 |
+
event.stopPropagation();
|
| 708 |
+
removeHistory(delBtn.dataset.del);
|
| 709 |
+
return;
|
| 710 |
+
}
|
| 711 |
+
const clearBtn = event.target.closest(".history-clear-all");
|
| 712 |
+
if (clearBtn) {
|
| 713 |
+
clearHistory();
|
| 714 |
+
return;
|
| 715 |
+
}
|
| 716 |
const item = event.target.closest("[data-query]");
|
| 717 |
if (!item) return;
|
| 718 |
DOM.searchInput.value = item.dataset.query;
|
|
|
|
| 720 |
syncUrl();
|
| 721 |
doSearch();
|
| 722 |
});
|
| 723 |
+
let historyLongPressTimer = null;
|
| 724 |
+
const cancelHistoryLongPress = () => {
|
| 725 |
+
if (historyLongPressTimer) {
|
| 726 |
+
clearTimeout(historyLongPressTimer);
|
| 727 |
+
historyLongPressTimer = null;
|
| 728 |
+
}
|
| 729 |
+
};
|
| 730 |
+
DOM.searchHistoryDropdown.addEventListener("mousedown", (event) => {
|
| 731 |
+
const item = event.target.closest("[data-query]");
|
| 732 |
+
if (!item || event.target.closest(".history-del") || event.target.closest(".history-clear-all")) return;
|
| 733 |
+
cancelHistoryLongPress();
|
| 734 |
+
historyLongPressTimer = setTimeout(() => {
|
| 735 |
+
removeHistory(item.dataset.query);
|
| 736 |
+
historyLongPressTimer = null;
|
| 737 |
+
}, 600);
|
| 738 |
+
});
|
| 739 |
+
DOM.searchHistoryDropdown.addEventListener("mouseup", cancelHistoryLongPress);
|
| 740 |
+
DOM.searchHistoryDropdown.addEventListener("mouseleave", cancelHistoryLongPress);
|
| 741 |
+
DOM.searchHistoryDropdown.addEventListener("touchstart", (event) => {
|
| 742 |
+
const item = event.target.closest("[data-query]");
|
| 743 |
+
if (!item || event.target.closest(".history-del") || event.target.closest(".history-clear-all")) return;
|
| 744 |
+
cancelHistoryLongPress();
|
| 745 |
+
historyLongPressTimer = setTimeout(() => {
|
| 746 |
+
removeHistory(item.dataset.query);
|
| 747 |
+
historyLongPressTimer = null;
|
| 748 |
+
}, 650);
|
| 749 |
+
}, { passive: true });
|
| 750 |
+
DOM.searchHistoryDropdown.addEventListener("touchend", cancelHistoryLongPress, { passive: true });
|
| 751 |
+
DOM.searchHistoryDropdown.addEventListener("touchmove", cancelHistoryLongPress, { passive: true });
|
| 752 |
let timer = null;
|
| 753 |
DOM.searchInput.addEventListener("input", () => {
|
| 754 |
clearTimeout(timer);
|
|
|
|
| 756 |
STATE.query = DOM.searchInput.value.trim();
|
| 757 |
STATE.page = 1;
|
| 758 |
addHistory(STATE.query);
|
| 759 |
+
renderHistoryDropdown();
|
| 760 |
syncUrl();
|
| 761 |
doSearch();
|
| 762 |
}, 250);
|
static/style.css
CHANGED
|
@@ -307,6 +307,40 @@ ul, li {
|
|
| 307 |
text-overflow: ellipsis;
|
| 308 |
white-space: nowrap;
|
| 309 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
.search-history-dropdown .history-del {
|
| 311 |
flex-shrink: 0;
|
| 312 |
background: none;
|
|
|
|
| 307 |
text-overflow: ellipsis;
|
| 308 |
white-space: nowrap;
|
| 309 |
}
|
| 310 |
+
.search-history-dropdown .history-del {
|
| 311 |
+
flex-shrink: 0;
|
| 312 |
+
width: 22px;
|
| 313 |
+
height: 22px;
|
| 314 |
+
border-radius: 999px;
|
| 315 |
+
color: var(--on-surface-variant);
|
| 316 |
+
display: inline-flex;
|
| 317 |
+
align-items: center;
|
| 318 |
+
justify-content: center;
|
| 319 |
+
font-size: 14px;
|
| 320 |
+
opacity: 0.65;
|
| 321 |
+
}
|
| 322 |
+
.search-history-dropdown .history-del:hover,
|
| 323 |
+
.search-history-dropdown .history-del:focus-visible {
|
| 324 |
+
background: var(--surface-variant);
|
| 325 |
+
color: var(--primary);
|
| 326 |
+
opacity: 1;
|
| 327 |
+
}
|
| 328 |
+
.search-history-dropdown .history-footer {
|
| 329 |
+
padding: 6px 12px;
|
| 330 |
+
border-top: 1px solid var(--outline-variant);
|
| 331 |
+
display: flex;
|
| 332 |
+
justify-content: center;
|
| 333 |
+
}
|
| 334 |
+
.search-history-dropdown .history-clear-all {
|
| 335 |
+
font-size: 12px;
|
| 336 |
+
color: var(--primary);
|
| 337 |
+
padding: 4px 8px;
|
| 338 |
+
border-radius: 6px;
|
| 339 |
+
}
|
| 340 |
+
.search-history-dropdown .history-clear-all:hover,
|
| 341 |
+
.search-history-dropdown .history-clear-all:focus-visible {
|
| 342 |
+
background: var(--surface-variant);
|
| 343 |
+
}
|
| 344 |
.search-history-dropdown .history-del {
|
| 345 |
flex-shrink: 0;
|
| 346 |
background: none;
|