Skip to content

Commit 873c9cc

Browse files
Merge pull request steam-bell-92#1586 from IshitaPrajapati08/feature/search-history-ui-redesign
[Feature]: Enhance Search History UI with Interactive Recent Searches steam-bell-92#1442
2 parents 8543888 + ed111df commit 873c9cc

8 files changed

Lines changed: 2329 additions & 274 deletions

File tree

web-app/css/styles.css

Lines changed: 1892 additions & 118 deletions
Large diffs are not rendered by default.

web-app/games.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,13 @@
159159
<div class="spinner"></div>
160160
</div>
161161
<div class="recent-searches-section" id="recentSearchesSection">
162-
<div class="dropdown-section-title">Recent</div>
163-
<div class="dropdown-list" id="recentSearchesList"></div>
162+
<div class="dropdown-section-title recent-searches-header">
163+
<span class="recent-searches-heading">
164+
<i class="fas fa-history" aria-hidden="true"></i>
165+
Recent Searches
166+
</span>
167+
</div>
168+
<div class="recent-searches-chips" id="recentSearchesList" role="list"></div>
164169
</div>
165170
<div
166171
class="results-section"

web-app/index.html

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,87 @@
5656
<!-- Cursor Glow -->
5757
<div aria-hidden="true" class="cursor-glow" id="cursorGlow"></div>
5858

59+
<!-- Navigation -->
60+
<nav aria-label="Primary" class="navbar" id="mainNavbar">
61+
<div class="nav-island">
62+
<div class="navbar-brand">
63+
<a class="logo-wrap" href="#">
64+
<img alt="PyMini Logo" class="navbar-logo" src="assets/favicon.svg" / loading="lazy">
65+
<div class="logo-text">
66+
<span class="logo-py">py</span>
67+
<span class="logo-mini">.mini</span>
68+
<span class="logo-parens">()</span>
69+
</div>
70+
<span class="logo-tagline">games, math &amp; terminal toys</span>
71+
</a>
72+
</div>
73+
<div class="search-box">
74+
<i class="fas fa-search"></i>
75+
<input aria-label="Search projects" id="navSearchInput" placeholder="Search projects or games..." type="text" />
76+
</div>
77+
<div class="nav-controls" id="navControls">
78+
<button aria-label="Mute sounds" class="sound-toggle" type="button">
79+
<i class="fas fa-volume-up"></i>
80+
</button>
81+
<button aria-label="Switch theme" class="theme-toggle" type="button">
82+
<i class="fas fa-moon"></i>
83+
</button>
84+
</div>
85+
</div>
86+
</nav>
87+
88+
<aside class="sidebar-dock" id="mainSidebar">
89+
<button class="sidebar-mobile-close" id="sidebarMobileClose" aria-label="Close sidebar" type="button">
90+
<i class="fas fa-times" aria-hidden="true"></i>
91+
</button>
92+
93+
<div class="sidebar-search">
94+
<div class="search-box" style="position: relative">
95+
<i class="fas fa-search" aria-hidden="true"></i>
96+
<input aria-label="Search projects" id="searchInput" placeholder="Search projects or tags..." type="text"
97+
role="combobox" aria-autocomplete="list" aria-controls="resultsList" aria-expanded="false" />
98+
<div class="search-dropdown" id="searchDropdown" aria-live="polite">
99+
<div class="dropdown-content" id="searchDropdownContent">
100+
<div class="search-loader" id="searchLoader" style="display: none">
101+
<div class="spinner"></div>
102+
</div>
103+
<div class="recent-searches-chips" id="recentSearchesDropdownList" role="list"></div>
104+
</div>
105+
<div class="results-section" id="resultsSection" style="display: none">
106+
<div class="dropdown-section-title">Projects</div>
107+
<div class="dropdown-list" id="resultsList" role="listbox"></div>
108+
</div>
109+
<div class="tips-section" id="tipsSection">
110+
<p><strong>Search tips</strong></p>
111+
<ul>
112+
<li>
113+
Type a project name — <code>snake</code>, <code>quiz</code>
114+
</li>
115+
<li>
116+
Filter by tag — <code>game</code>, <code>math</code>,
117+
<code>utility</code>
118+
</li>
119+
</ul>
120+
</div>
121+
<div class="no-results-message" id="noResultsMessage" style="display: none">
122+
No projects found. Try a different keyword.
123+
</div>
124+
</div>
125+
</div>
126+
</div>
127+
</div>
128+
<div class="recent-searches-section" id="recentSearchesSection">
129+
<div class="dropdown-section-title recent-searches-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
130+
<span class="recent-searches-heading" style="color: #e2e8f0; font-weight: 500;">
131+
<i class="fas fa-history" aria-hidden="true" style="margin-right: 6px; color: #a78bfa;"></i>
132+
Search History
133+
</span>
134+
<button id="clearRecentBtn" class="recent-clear-all-btn" aria-label="Clear recent searches" style="background: none; border: none; color: #ef4444; font-size: 0.85rem; cursor: pointer; display: flex; align-items: center; gap: 5px;">
135+
<i class="fas fa-trash-alt"></i> Clear History
136+
</button>
137+
</div>
138+
<div class="recent-searches-chips" id="recentSearchesList" role="list"></div>
139+
</div>
59140
<!-- SIDEBAR - Fixed Position on Left -->
60141
<aside class="sidebar" id="mainSidebar">
61142
<div class="sidebar-menu-wrapper">

web-app/js/main.js

Lines changed: 91 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,24 @@ function safeRun(fn) {
2020
console.error(e);
2121
}
2222
}
23+
// Import the initSearch function from your updated search module
24+
import { initSearch } from "./modules/search.js";
2325

26+
document.addEventListener("DOMContentLoaded", () => {
27+
// Other initializations...
28+
29+
// Initialize the search bar
30+
initSearch((query) => {
31+
// This callback runs whenever a search is triggered
32+
console.log("Searching for:", query);
33+
34+
// If you have a function that filters projects globally, call it here.
35+
// Example:
36+
// if (typeof window.applySearchFilter === 'function') {
37+
// window.applySearchFilter(query);
38+
// }
39+
});
40+
});
2441
function debounce(fn, ms) {
2542
var timer;
2643
return function () {
@@ -917,81 +934,91 @@ document.addEventListener("DOMContentLoaded", function () {
917934
if (searchDropdown) searchDropdown.classList.remove("active");
918935
}
919936

937+
function wireClearRecentBtn() {
938+
var headerClearBtn = document.getElementById("clearRecentBtn");
939+
if (!headerClearBtn) return;
940+
headerClearBtn.style.display = recentSearches.length
941+
? "inline-flex"
942+
: "none";
943+
headerClearBtn.onclick = function (e) {
944+
e.stopPropagation();
945+
if (!recentSearches || recentSearches.length === 0) return;
946+
showConfirm(
947+
"Clear all recent searches? This cannot be undone.",
948+
function () {
949+
recentSearches = [];
950+
localStorage.setItem(
951+
"recentSearches",
952+
JSON.stringify(recentSearches)
953+
);
954+
renderRecentSearches();
955+
closeDropdown();
956+
},
957+
function () {}
958+
);
959+
};
960+
}
961+
920962
function renderRecentSearches() {
921963
if (noResultsMessage) noResultsMessage.style.display = "none";
922964
if (!recentSearchesSection) return;
923965

924-
if (recentSearches.length === 0) {
925-
recentSearchesSection.style.display = "none";
926-
if (tipsSection) tipsSection.style.display = "block";
927-
if (resultsSection) resultsSection.style.display = "none";
928-
return;
929-
}
930-
931966
if (recentSearchesList) {
932967
recentSearchesList.innerHTML = "";
933-
recentSearches.slice(0, 5).forEach(function (search) {
934-
var item = document.createElement("div");
935-
item.className = "dropdown-recent-item";
936-
var text = document.createElement("div");
937-
text.className = "dropdown-recent-text";
938968

939-
var clock = document.createElement("i");
940-
clock.className = "fas fa-history";
941-
clock.style.opacity = "0.5";
942-
clock.style.fontSize = "0.8rem";
943-
944-
var label = document.createElement("span");
945-
label.textContent = search;
946-
947-
text.append(clock, label);
948-
949-
var removeBtn = document.createElement("button");
950-
removeBtn.className = "dropdown-recent-remove";
951-
removeBtn.setAttribute("aria-label", "Remove search");
952-
removeBtn.innerHTML = '<i class="fas fa-times"></i>';
953-
954-
item.append(text, removeBtn);
955-
956-
label.addEventListener("click", function () {
957-
syncSearchInputs(search, searchInput);
958-
currentSearchQuery = search;
959-
performSearch(true);
960-
closeDropdown();
961-
});
962-
963-
removeBtn.addEventListener("click", function (e) {
964-
e.stopPropagation();
965-
recentSearches = recentSearches.filter(function (s) {
966-
return s !== search;
969+
if (recentSearches.length === 0) {
970+
var emptyEl = document.createElement("p");
971+
emptyEl.className = "recent-searches-empty";
972+
emptyEl.textContent =
973+
"No recent searches yet. Start exploring projects!";
974+
recentSearchesList.appendChild(emptyEl);
975+
} else {
976+
recentSearches.slice(0, 5).forEach(function (search) {
977+
var chip = document.createElement("div");
978+
chip.className = "recent-search-chip";
979+
chip.setAttribute("role", "listitem");
980+
981+
var labelBtn = document.createElement("button");
982+
labelBtn.type = "button";
983+
labelBtn.className = "recent-search-chip-label";
984+
labelBtn.setAttribute("aria-label", "Search for " + search);
985+
var labelSpan = document.createElement("span");
986+
labelSpan.textContent = search;
987+
labelBtn.appendChild(labelSpan);
988+
989+
var removeBtn = document.createElement("button");
990+
removeBtn.type = "button";
991+
removeBtn.className = "recent-search-chip-remove";
992+
removeBtn.setAttribute("aria-label", "Remove search");
993+
removeBtn.innerHTML =
994+
'<i class="fas fa-times" aria-hidden="true"></i>';
995+
996+
chip.append(labelBtn, removeBtn);
997+
998+
labelBtn.addEventListener("click", function () {
999+
syncSearchInputs(search, searchInput);
1000+
currentSearchQuery = search;
1001+
performSearch(true);
1002+
closeDropdown();
9671003
});
968-
localStorage.setItem(
969-
"recentSearches",
970-
JSON.stringify(recentSearches)
971-
);
972-
renderRecentSearches();
973-
});
9741004

975-
recentSearchesList.appendChild(item);
976-
});
977-
978-
// Wire up header Clear All button (if present)
979-
var headerClearBtn = document.getElementById('clearRecentBtn');
980-
if (headerClearBtn) {
981-
headerClearBtn.style.display = recentSearches.length ? 'inline-flex' : 'none';
982-
headerClearBtn.onclick = function (e) {
983-
e.stopPropagation();
984-
if (!recentSearches || recentSearches.length === 0) return;
985-
showConfirm('Clear all recent searches? This cannot be undone.', function () {
986-
recentSearches = [];
987-
localStorage.setItem('recentSearches', JSON.stringify(recentSearches));
1005+
removeBtn.addEventListener("click", function (e) {
1006+
e.stopPropagation();
1007+
recentSearches = recentSearches.filter(function (s) {
1008+
return s !== search;
1009+
});
1010+
localStorage.setItem(
1011+
"recentSearches",
1012+
JSON.stringify(recentSearches)
1013+
);
9881014
renderRecentSearches();
989-
closeDropdown();
990-
}, function () {
991-
// cancelled
9921015
});
993-
};
1016+
1017+
recentSearchesList.appendChild(chip);
1018+
});
9941019
}
1020+
1021+
wireClearRecentBtn();
9951022
}
9961023

9971024
recentSearchesSection.style.display = "block";
@@ -1420,41 +1447,7 @@ document.addEventListener("DOMContentLoaded", function () {
14201447
removeTrap = null;
14211448
}
14221449

1423-
recentSearchesList.innerHTML = '';
1424-
recentSearches.slice(0, 5).forEach((search) => {
1425-
const item = document.createElement('div');
1426-
item.className = 'dropdown-recent-item';
1427-
item.innerHTML = `
1428-
<button type="button" class="dropdown-recent-text" aria-label="Search ${search}">
1429-
<i class="fas fa-history" style="opacity: 0.5; font-size: 0.9rem;"></i>
1430-
<span style="flex: 1; color: var(--text-secondary);">${search}</span>
1431-
</button>
1432-
<button type="button" class="dropdown-recent-remove" aria-label="Remove search">
1433-
<i class="fas fa-x"></i>
1434-
</button>
1435-
`;
1436-
1437-
const textButton = item.querySelector('.dropdown-recent-text');
1438-
const removeBtn = item.querySelector('.dropdown-recent-remove');
1439-
1440-
if (textButton) {
1441-
textButton.addEventListener('click', () => {
1442-
searchInput.value = search;
1443-
currentSearchQuery = search;
1444-
performSearch();
1445-
closeDropdown();
1446-
});
1447-
}
1448-
1449-
if (removeBtn) {
1450-
removeBtn.addEventListener('click', (e) => {
1451-
e.stopPropagation();
1452-
recentSearches = recentSearches.filter(s => s !== search);
1453-
localStorage.setItem('recentSearches', JSON.stringify(recentSearches));
1454-
renderRecentSearches();
1455-
});
1456-
}
1457-
});
1450+
renderRecentSearches();
14581451
// Clear content
14591452
if (modalBody) {
14601453
modalBody.innerHTML = "";

0 commit comments

Comments
 (0)