feat: Add bottom navigation for OPDS modal and enhance styling for better visibility

This commit is contained in:
JB
2025-11-28 12:13:37 -08:00
parent 775e05f94c
commit 7cc60aacf6
4 changed files with 83 additions and 33 deletions
+34 -27
View File
@@ -5,6 +5,7 @@ if (modal && browser) {
const statusEl = browser.querySelector('[data-role="opds-status"]');
const resultsEl = browser.querySelector('[data-role="opds-results"]');
const navEl = browser.querySelector('[data-role="opds-nav"]');
const navBottomEl = browser.querySelector('[data-role="opds-nav-bottom"]');
const alphaPickerEl = browser.querySelector('[data-role="opds-alpha-picker"]');
const tabsEl = modal.querySelector('[data-role="opds-tabs"]');
const searchForm = modal.querySelector('[data-role="opds-search"]');
@@ -247,17 +248,10 @@ if (modal && browser) {
if (!alphaPickerEl || state.query) {
return false;
}
if (!Array.isArray(entries) || entries.length < 6) {
if (!Array.isArray(entries) || entries.length === 0) {
return false;
}
const mode = deriveBrowseMode();
const counts = stats || computeEntryStats(entries);
const hasNavigation = counts[EntryTypes.NAVIGATION] > 0;
if (mode === 'generic' && !hasNavigation) {
return false;
}
const alphabetCounts = collectAlphabetCounts(entries);
return alphabetCounts.size > 1;
return true;
};
const refreshAlphabetActiveState = () => {
@@ -542,10 +536,14 @@ if (modal && browser) {
};
const renderNav = (links) => {
if (!navEl) {
const targets = [navEl, navBottomEl].filter(Boolean);
if (!targets.length) {
return;
}
navEl.innerHTML = '';
targets.forEach((el) => {
el.innerHTML = '';
});
const descriptors = [
{ key: 'up', label: 'Up one level' },
{ key: 'previous', label: 'Previous page' },
@@ -556,26 +554,35 @@ if (modal && browser) {
return;
}
const link = resolveRelLink(links, key) || resolveRelLink(links, `/${key}`);
const button = document.createElement('button');
button.type = 'button';
button.className = 'button button--ghost';
button.textContent = label;
const hasLink = Boolean(link && link.href);
if (hasLink) {
button.addEventListener('click', () => {
const targetQuery = key === 'up' ? '' : state.query;
const tabId = resolveTabIdForHref(link.href);
loadFeed({ href: link.href, query: targetQuery, activeTab: tabId || (targetQuery ? TabIds.SEARCH : TabIds.CUSTOM) });
});
} else if (key === 'previous') {
button.disabled = true;
button.setAttribute('aria-disabled', 'true');
} else {
if (!hasLink && key !== 'previous') {
return;
}
navEl.appendChild(button);
targets.forEach((targetEl) => {
const button = document.createElement('button');
button.type = 'button';
button.className = 'button button--ghost';
button.textContent = label;
if (hasLink) {
button.addEventListener('click', () => {
const targetQuery = key === 'up' ? '' : state.query;
const tabId = resolveTabIdForHref(link.href);
loadFeed({ href: link.href, query: targetQuery, activeTab: tabId || (targetQuery ? TabIds.SEARCH : TabIds.CUSTOM) });
});
} else if (key === 'previous') {
button.disabled = true;
button.setAttribute('aria-disabled', 'true');
}
targetEl.appendChild(button);
});
});
targets.forEach((el) => {
el.hidden = !el.childElementCount;
});
navEl.hidden = !navEl.childElementCount;
};
const createEntry = (entry) => {
+16 -1
View File
@@ -654,7 +654,22 @@ body {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
margin: 0.35rem 0 0.65rem;
margin: 0;
padding: 0.75rem 0 1rem;
position: sticky;
top: 0;
z-index: 20;
background: var(--bg);
box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.2);
margin-bottom: 1rem;
border-bottom: 1px solid var(--panel-border);
}
.opds-modal__nav--bottom {
margin-top: 1rem;
padding-top: 1.5rem;
border-top: 1px solid var(--panel-border);
justify-content: center;
}
.opds-alpha-picker__button {
+1
View File
@@ -76,6 +76,7 @@
<div class="opds-modal__results-wrap">
<ul class="opds-modal__results" data-role="opds-results"></ul>
</div>
<div class="opds-modal__nav opds-modal__nav--bottom" data-role="opds-nav-bottom"></div>
</div>
</div>
</div>