diff --git a/js/index.js b/js/index.js
index d0136b3..42a3ff0 100644
--- a/js/index.js
+++ b/js/index.js
@@ -212,9 +212,8 @@ if (Const.APP_DATES_CSV) httpGet(Const.APP_DATES_CSV).then(csv=>{
appSortInfo[key].created = parseDate(l[1]);
appSortInfo[key].modified = parseDate(l[2]);
});
- document.querySelector(".sort-nav").classList.remove("hidden");
- document.querySelector(".sort-nav label[sortid='created']").classList.remove("hidden");
- document.querySelector(".sort-nav label[sortid='modified']").classList.remove("hidden");
+ document.querySelector(".sort-nav a[sortid='created']").parentElement.classList.remove("hidden");
+ document.querySelector(".sort-nav a[sortid='modified']").parentElement.classList.remove("hidden");
}).catch(err=>{
console.log("No recent.csv - app sort disabled");
});
@@ -716,9 +715,6 @@ function getAppHTML(app, appInstalled, forInterface) {
// =========================================== Library
-// Can't use chip.attributes.filterid.value here because Safari/Apple's WebView doesn't handle it
-let chips = Array.from(document.querySelectorAll('.filter-nav .chip')).map(chip => chip.getAttribute("filterid"));
-
/*
Filter types:
.../BangleApps/#blue shows apps having "blue" in app.id or app.tag --> searchType:hash
@@ -731,14 +727,27 @@ let chips = Array.from(document.querySelectorAll('.filter-nav .chip')).map(chip
*/
-let activeSort = '';
+let activeSort = 'favourites';
let libraryShowAll = false; // perist whether user chose to view all apps
// Update the sort state to match the current sort value
function refreshSort(){
let sortContainer = document.querySelector("#librarycontainer .sort-nav");
- sortContainer.querySelector('.active').classList.remove('active');
- if(activeSort) sortContainer.querySelector('.chip[sortid="'+activeSort+'"]').classList.add('active');
- else sortContainer.querySelector('.chip[sortid]').classList.add('active');
+ let sortToggle = sortContainer.querySelector('.dropdown-toggle span');
+ let sortAnchors = sortContainer.querySelectorAll('.menu-item a');
+
+ // Find the currently selected sort and update label
+ let activeAnchor = Array.from(sortAnchors).find(a =>
+ a.getAttribute('sortid') === (activeSort || '')
+ );
+
+ if (activeAnchor && sortToggle) {
+ sortToggle.innerHTML = '';
+ if (activeSort === ''||!activeSort) {
+ sortToggle.innerHTML += `None`;
+ } else {
+ sortToggle.innerHTML += activeAnchor.textContent;
+ }
+ }
}
function handlefavouriteClick(icon,app,button){
const favAnimMS = 500; // duration of favourite animation in ms
@@ -787,18 +796,25 @@ function refreshLibrary(options) {
searchChip = searchParams.get("c").toLowerCase();
}
}
- if (searchType === "hash" && chips.indexOf(searchValue)>=0) {
+ if (searchType === "hash") {
+ // Treat URL hash as a chip/filter identifier
+ searchChip = searchValue;
searchType = "";
searchValue = "";
- searchChip = searchValue;
}
- // Update the 'chips' to match the current window location
let filtersContainer = document.querySelector("#librarycontainer .filter-nav");
- filtersContainer.querySelector('.active').classList.remove('active');
- if(searchChip) {
- let hashFilter = filtersContainer.querySelector('.chip[filterid="'+searchChip+'"]');
- if (hashFilter) hashFilter.classList.add('active');
- } else filtersContainer.querySelector('.chip[filterid]').classList.add('active');
+ let filterToggle = filtersContainer.querySelector('.dropdown-toggle span');
+ let filterAnchors = filtersContainer.querySelectorAll('.menu-item a');
+
+ // Find the currently selected filter and update label
+ let activeFilterAnchor = Array.from(filterAnchors).find(a =>
+ a.getAttribute('dt') === (searchChip || '')
+ );
+
+ if (activeFilterAnchor && filterToggle) {
+ filterToggle.innerHTML = '';
+ filterToggle.innerHTML += activeFilterAnchor.textContent;
+ }
// update the search box value
if (!options.dontChangeSearchBox) {
if (searchType === "full")
@@ -872,14 +888,25 @@ function refreshLibrary(options) {
}).map(a => a.app);
}
// if not otherwise sorted, use 'sort by' option
- if (!sortedByRelevance)
- visibleApps.sort(appSorter);
+ if (!sortedByRelevance) {
+ if (activeSort === 'random') {
+ // Shuffle apps for "Explore" mode using Fisher-Yates
+ for (let i = visibleApps.length - 1; i > 0; i--) {
+ let j = Math.floor(Math.random() * (i + 1));
+ let t = visibleApps[i]; visibleApps[i] = visibleApps[j]; visibleApps[j] = t;
+ }
+ } else {
+ visibleApps.sort(appSorter);
+ }
+ }
if (activeSort && !sortedByRelevance) { // only sort if not searching (searching already sorts)
if (["created","modified","installs","favourites"].includes(activeSort)) {
visibleApps = visibleApps.sort((a,b) =>
((appSortInfo[b.id]||{})[activeSort]||0) -
((appSortInfo[a.id]||{})[activeSort]||0));
+ } else if (activeSort === 'random') {
+ // nothing to do - shuffled above
} else throw new Error("Unknown sort type "+activeSort);
}
@@ -1450,19 +1477,21 @@ connectMyDeviceBtn.addEventListener("click", () => {
});
Comms.watchConnectionChange(handleConnectionChange);
-// Handle the 'chips'
let filtersContainer = document.querySelector("#librarycontainer .filter-nav");
filtersContainer.addEventListener('click', ({ target }) => {
- if (target.classList.contains('active')) return;
- let filterName = target.getAttribute('filterid') || '';
- // Update window URL
+ // Only handle anchor clicks in menu items
+ if (target.tagName !== 'A' || !target.hasAttribute('dt')) return;
+
+ let filterName = target.getAttribute('dt') || '';
window.history.replaceState(null, null, "?c=" + filterName);
refreshLibrary();
});
let sortContainer = document.querySelector("#librarycontainer .sort-nav");
sortContainer.addEventListener('click', ({ target }) => {
- if (target.classList.contains('active')) return;
+ // Only handle anchor clicks in menu items
+ if (target.tagName !== 'A' || !target.hasAttribute('sortid')) return;
+
activeSort = target.getAttribute('sortid') || '';
refreshSort();
refreshLibrary();
@@ -1515,6 +1544,70 @@ settingsCheckbox("settings-alwaysAllowEmulator", "alwaysAllowEmulator");
settingsCheckbox("settings-autoReload", "autoReload");
settingsCheckbox("settings-nopacket", "noPackets");
loadSettings();
+refreshSort();
+
+
+function autoAlignMenu(dropdown) {
+ const menu = dropdown.querySelector('.menu');
+ if (!menu) return;
+
+ // Ensure we can measure even if hidden
+ const prevVis = menu.style.visibility;
+ const prevDisp = menu.style.display;
+ const cs = getComputedStyle(menu);
+ if (cs.display === 'none') {
+ menu.style.visibility = 'hidden';
+ menu.style.display = 'block';
+ }
+
+ // Reset to left before measuring
+ menu.classList.remove('align-right');
+
+ const rect = menu.getBoundingClientRect();
+ const overflowRight = rect.right > (window.innerWidth - 8); // 8px margin
+ if (overflowRight) menu.classList.add('align-right');
+
+ // Restore styles
+ menu.style.visibility = prevVis || '';
+ menu.style.display = prevDisp || '';
+}
+
+function closeDropdowns() {
+ document.querySelectorAll('.dropdown.active').forEach(dropdown => {
+ dropdown.classList.remove('active');
+ const toggle = dropdown.querySelector('.dropdown-toggle');
+ if (toggle && document.activeElement === toggle) {
+ requestAnimationFrame(() => toggle.blur());
+ }
+ });
+}
+
+// Toggle dropdowns on header click
+document.addEventListener('click', (e) => {
+ const toggle = e.target.closest('.dropdown-toggle');
+ if (toggle) {
+ e.preventDefault();
+ const dropdown = toggle.closest('.dropdown');
+ if (!dropdown) return;
+ const shouldOpen = !dropdown.classList.contains('active');
+ closeDropdowns();
+ if (shouldOpen) {
+ dropdown.classList.add('active');
+ requestAnimationFrame(() => autoAlignMenu(dropdown));
+ }
+ return;
+ }
+
+ closeDropdowns();
+});
+
+// Keep alignment on resize
+window.addEventListener('resize', () => {
+ document.querySelectorAll('.dropdown .menu').forEach(menu => {
+ const dropdown = menu.closest('.dropdown');
+ if (dropdown) autoAlignMenu(dropdown);
+ });
+});
let btn;