mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-10-04 13:11:13 +01:00
Add further responsive UI using uiKit (#36)
Utilising [UiKit](https://getuikit.com/) as the CSS framework Adjusted existing functionality & CSS to integrate with UiKits - should retain same functionality - Ui colors are UiKit defaults - Search results automatically expand when searching - UiKit ensures its automatically responsive for mobile ### **Search results**  ### **Details dialog**   **- The dialog could be improved further by adjusting to a modal in UiKit purely, however wasn't sure on how to do / time reqs.** ### **Search Results & Download Status are in expandable accordions**   ### **Adjusted search to UiKits with clickable search icon**  ### **Added gitHub link to footer and increased font sizing**  ### **Mobile views (Chrome on Desktop) - scales & retains scroll**    ### **TODO:** - Improvements to modal functionality - Pagination - Cleaning (removing unused CSS etc)
This commit is contained in:
+1
-28
@@ -33,12 +33,6 @@ header {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
@@ -73,27 +67,6 @@ footer {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-container button,
|
||||
.download-button {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-container button:hover,
|
||||
.download-button:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.table-responsive {
|
||||
margin-bottom: 1rem;
|
||||
@@ -214,7 +187,7 @@ tbody tr:hover {
|
||||
|
||||
.details-container {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
padding: 0.25rem 0.25rem 2rem 3rem;
|
||||
border-radius: 4px;
|
||||
max-width: 800px;
|
||||
width: 90%;
|
||||
|
||||
+28
-20
@@ -47,11 +47,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
},
|
||||
|
||||
showLoading(element) {
|
||||
element.style.display = 'block';
|
||||
element.removeAttribute('hidden');
|
||||
},
|
||||
|
||||
hideLoading(element) {
|
||||
element.style.display = 'none';
|
||||
element.setAttribute('hidden', '');
|
||||
},
|
||||
|
||||
showAccordion(element) {
|
||||
UIkit.accordion(element).toggle(1, true);
|
||||
},
|
||||
|
||||
async fetchJson(url, options = {}) {
|
||||
@@ -90,9 +94,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
try {
|
||||
STATE.isSearching = true;
|
||||
elements.resultsSection.classList.remove('collapsed');
|
||||
utils.showLoading(elements.searchLoading);
|
||||
|
||||
utils.showAccordion(elements.resultsSection);
|
||||
const data = await utils.fetchJson(
|
||||
`${API_ENDPOINTS.search}?query=${encodeURIComponent(query)}`
|
||||
);
|
||||
@@ -161,12 +165,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
createActionCell(book) {
|
||||
const buttonDetails = utils.createElement('button', {
|
||||
className: 'details-button',
|
||||
className: 'uk-button uk-button-default uk-align-center uk-margin-small',
|
||||
onclick: () => bookDetails.show(book.id)
|
||||
}, [utils.createElement('span', { textContent: 'Details' })]);
|
||||
|
||||
const downloadButton = utils.createElement('button', {
|
||||
className: 'download-button',
|
||||
className: 'uk-button uk-button-primary uk-align-center uk-margin-small',
|
||||
onclick: () => bookDetails.downloadBook(book)
|
||||
}, [utils.createElement('span', { textContent: 'Download' })]);
|
||||
|
||||
@@ -221,9 +225,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
generateDetailsHTML(book) {
|
||||
return `
|
||||
<div class="details-header">
|
||||
<img src="${book.preview || ''}" alt="Book Preview">
|
||||
<div class="details-info">
|
||||
|
||||
<div class="uk-card uk-card-default uk-child-width-1-2" uk-grid>
|
||||
<div class="uk-card-media-left uk-cover-container uk-padding">
|
||||
<img class="uk-height-medium" src="${book.preview || ''}" alt="Book Preview" uk-cover>
|
||||
<canvas width="299" height="461"></canvas>
|
||||
</div>
|
||||
<div class="uk-card-body">
|
||||
<h3>${book.title || 'No title available'}</h3>
|
||||
<p><strong>Author:</strong> ${book.author || 'N/A'}</p>
|
||||
<p><strong>Publisher:</strong> ${book.publisher || 'N/A'}</p>
|
||||
@@ -232,12 +240,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<p><strong>Format:</strong> ${book.format || 'N/A'}</p>
|
||||
<p><strong>Size:</strong> ${book.size || 'N/A'}</p>
|
||||
</div>
|
||||
|
||||
<button id="download-button" class="uk-button uk-button-primary" type="button">Download</button>
|
||||
<button id="close-details" class="uk-button uk-button-default uk-modal-close" type="button">Close</button>
|
||||
</div>
|
||||
${this.generateInfoList(book.info)}
|
||||
<div class="details-actions">
|
||||
<button id="download-button">Download</button>
|
||||
<button id="close-details">Close</button>
|
||||
</div>
|
||||
<ul uk-accordion>
|
||||
<li>
|
||||
<a class="uk-accordion-title" href>Further Information</a>
|
||||
<div class="uk-accordion-content">
|
||||
${this.generateInfoList(book.info)}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
},
|
||||
|
||||
@@ -250,7 +264,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
`)
|
||||
.join('');
|
||||
|
||||
return `<ul class="details-info-list">${listItems}</ul>`;
|
||||
return `<ul class="uk-list uk-list-bullet">${listItems}</ul>`;
|
||||
},
|
||||
|
||||
async downloadBook(book) {
|
||||
@@ -263,7 +277,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
);
|
||||
|
||||
modal.close();
|
||||
elements.resultsSection.classList.add('collapsed');
|
||||
status.fetch();
|
||||
} catch (error) {
|
||||
console.error('Download error:', error);
|
||||
@@ -405,11 +418,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Results section toggle
|
||||
elements.resultsHeading.addEventListener('click', () => {
|
||||
elements.resultsSection.classList.toggle('collapsed');
|
||||
});
|
||||
|
||||
// Modal close on overlay click
|
||||
elements.modalOverlay.addEventListener('click', (e) => {
|
||||
if (e.target === elements.modalOverlay) {
|
||||
|
||||
+88
-77
@@ -9,97 +9,108 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
<!-- Add favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<!-- UIkit CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.21.16/dist/css/uikit.min.css" />
|
||||
|
||||
<!-- UIkit JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.21.16/dist/js/uikit.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.21.16/dist/js/uikit-icons.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Book Search & Download</h1>
|
||||
<h1 class="uk-heading-medium" style="color: white;">Book Search & Download</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Search Section -->
|
||||
<section class="search-section">
|
||||
<div class="search-container">
|
||||
<input
|
||||
type="text"
|
||||
id="search-input"
|
||||
placeholder="Search by ISBN, title, author..."
|
||||
aria-label="Search books"
|
||||
>
|
||||
<button id="search-button" aria-label="Search">
|
||||
<span>Search</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<div class="uk-container">
|
||||
<!-- Search Section -->
|
||||
<section class="uk-section-xxsmall uk-padding">
|
||||
<div class="uk-container uk-width-expand">
|
||||
<form class="uk-search uk-search-default uk-width-expand">
|
||||
<input class="uk-search-input" type="search" placeholder="Search by ISBN, title, author..." aria-label="Search books" id="search-input">
|
||||
<button class="uk-search-icon-flip" uk-search-icon id="search-button" type="button"></button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Results Section -->
|
||||
<section class="results-section collapsed" id="results-section">
|
||||
<h2 id="results-heading">
|
||||
Search Results
|
||||
<span class="toggle-icon">▼</span>
|
||||
</h2>
|
||||
<div class="loading-indicator" id="search-loading" role="status">
|
||||
<span class="spinner"></span>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
<div class="results-content">
|
||||
<div class="table-responsive">
|
||||
<table id="results-table" role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Preview</th>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Author</th>
|
||||
<th scope="col">Publisher</th>
|
||||
<th scope="col">Year</th>
|
||||
<th scope="col">Language</th>
|
||||
<th scope="col">Format</th>
|
||||
<th scope="col">Size</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Search results will be injected here -->
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Results Section -->
|
||||
<ul uk-accordion id="results-section">
|
||||
<li id="search-accordion">
|
||||
<a class="uk-accordion-title" href><h1 class="uk-heading-xsmall">Search Results</h1></a>
|
||||
<div class="uk-accordion-content">
|
||||
<div id="search-loading" class="uk-flex uk-flex-center" role="status" hidden>
|
||||
<div uk-spinner="ratio: 2"></div>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
<div class="results-content">
|
||||
<div class="uk-overflow-auto">
|
||||
<table id="results-table" class="uk-table uk-table-hover uk-table-divider" role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Preview</th>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Author</th>
|
||||
<th scope="col">Publisher</th>
|
||||
<th scope="col">Year</th>
|
||||
<th scope="col">Language</th>
|
||||
<th scope="col">Format</th>
|
||||
<th scope="col">Size</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Search results will be injected here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Book Details Modal -->
|
||||
<div class="modal-overlay" id="modal-overlay" role="dialog" aria-modal="true">
|
||||
<div class="details-container" id="details-container">
|
||||
<!-- Details will be dynamically injected here -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Book Details Modal -->
|
||||
<div class="modal-overlay" id="modal-overlay" role="dialog" aria-modal="true">
|
||||
<div class="details-container" id="details-container">
|
||||
<!-- Details will be dynamically injected here -->
|
||||
</div>
|
||||
<!-- Status Section -->
|
||||
<ul uk-accordion id="status-section">
|
||||
<li id="search-accordion">
|
||||
<a class="uk-accordion-title" href><h1 class="uk-heading-xsmall">Download Status</h1></a>
|
||||
<div class="uk-accordion-content">
|
||||
<div id="status-loading" class="uk-flex uk-flex-center" role="status" hidden>
|
||||
<div uk-spinner="ratio: 2"></div>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
<div class="status-content">
|
||||
<div class="uk-overflow-auto">
|
||||
<table id="status-table" class="uk-table uk-table-hover uk-table-divider" role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Book ID</th>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Search results will be injected here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Status Section -->
|
||||
<section class="status-section">
|
||||
<h2>Download Status</h2>
|
||||
<div class="loading-indicator" id="status-loading" role="status">
|
||||
<span class="spinner"></span>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table id="status-table" role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Book ID</th>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Status information will be injected here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Calibre Web Book Downloader.</p>
|
||||
<h1 class="uk-heading-xxsmall" style="color: white;">Calibre Web Book Downloader</h1>
|
||||
<a href="https://github.com/calibrain/calibre-web-automated-book-downloader" class="uk-icon-link" uk-icon="icon:github; ratio: 2"></a>
|
||||
</footer>
|
||||
|
||||
<!-- Scripts -->
|
||||
|
||||
Reference in New Issue
Block a user