From 6dab60e2ea98c1bb0212991f8df57375106fc4a9 Mon Sep 17 00:00:00 2001 From: Daniel Dada Date: Fri, 6 Feb 2026 13:57:33 +0300 Subject: [PATCH] added pagination to popup --- popup/popup.css | 66 ++++++++++++++++++++++ popup/popup.html | 1 + src/popup/PopupController.ts | 106 ++++++++++++++++++++++++++++++++++- 3 files changed, 171 insertions(+), 2 deletions(-) diff --git a/popup/popup.css b/popup/popup.css index 618718f..22f57e3 100644 --- a/popup/popup.css +++ b/popup/popup.css @@ -645,6 +645,72 @@ body { text-align: center; } +/* Pagination (Words tab) */ +.pagination-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 8px; + padding: 6px 8px; + background: var(--section-bg); + border: 1px solid var(--input-border); + border-radius: 8px; + flex-shrink: 0; + flex-wrap: wrap; + font-size: 11px; +} + +.pagination-info { + font-size: 11px; + opacity: 0.8; + white-space: nowrap; +} + +.pagination-controls { + display: flex; + align-items: center; + gap: 4px; +} + +.pagination-btn { + background: var(--input-bg); + border: 1px solid var(--input-border); + color: var(--text-color); + border-radius: 6px; + padding: 3px 5px; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + min-width: 24px; + min-height: 24px; + font-size: 11px; +} + +.pagination-btn:hover:not(:disabled) { + background: var(--section-bg); + border-color: var(--accent); +} + +.pagination-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.pagination-pages { + display: flex; + align-items: center; + padding: 0 4px; +} + +.pagination-pages .page-info { + font-size: 11px; + opacity: 0.9; + font-weight: 500; +} + /* Word item 3-dot menu dropdown */ .word-item-menu-dropdown { display: none; diff --git a/popup/popup.html b/popup/popup.html index cb974ee..2a2e4d0 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -195,6 +195,7 @@
+
diff --git a/src/popup/PopupController.ts b/src/popup/PopupController.ts index c207911..0094d16 100644 --- a/src/popup/PopupController.ts +++ b/src/popup/PopupController.ts @@ -9,6 +9,9 @@ export class PopupController { private selectedCheckboxes = new Set(); private globalHighlightEnabled = true; private wordSearchQuery = ''; + private currentPage = 1; + private pageSize = 100; + private totalWords = 0; private matchCaseEnabled = false; private matchWholeEnabled = false; private exceptionsList: string[] = []; @@ -271,6 +274,7 @@ export class PopupController { wordSearch.addEventListener('input', (e) => { this.wordSearchQuery = (e.target as HTMLInputElement).value; + this.currentPage = 1; this.renderWords(); }); } @@ -1023,6 +1027,7 @@ export class PopupController { if (!Number.isNaN(index)) { this.selectedCheckboxes.clear(); this.currentListIndex = index; + this.currentPage = 1; this.renderWords(); this.updateListForm(); this.renderLists(); @@ -1063,14 +1068,25 @@ export class PopupController { filteredWords = list.words.filter(w => w.wordStr.toLowerCase().includes(q)); } + this.totalWords = filteredWords.length; + if (filteredWords.length === 0) { wordList.innerHTML = '
No words found
'; const wordCount = document.getElementById('wordCount'); if (wordCount) wordCount.textContent = '0'; + this.renderPaginationControls(); return; } - wordList.innerHTML = filteredWords.map(w => { + const totalPages = Math.ceil(this.totalWords / this.pageSize); + if (this.currentPage > totalPages) { + this.currentPage = Math.max(1, totalPages); + } + const startIndex = (this.currentPage - 1) * this.pageSize; + const endIndex = Math.min(startIndex + this.pageSize, this.totalWords); + const paginatedWords = filteredWords.slice(startIndex, endIndex); + + wordList.innerHTML = paginatedWords.map(w => { const realIndex = list.words.indexOf(w); const isSelected = this.selectedCheckboxes.has(realIndex); return this.createWordItemHTML(w, realIndex, isSelected); @@ -1078,8 +1094,94 @@ export class PopupController { const wordCount = document.getElementById('wordCount'); if (wordCount) { - wordCount.textContent = filteredWords.length.toString(); + wordCount.textContent = this.totalWords.toString(); } + + this.renderPaginationControls(); + } + + private renderPaginationControls(): void { + const paginationContainer = document.getElementById('paginationControls'); + if (!paginationContainer) return; + + const totalPages = Math.ceil(this.totalWords / this.pageSize); + + if (totalPages <= 1) { + paginationContainer.style.display = 'none'; + return; + } + + const startItem = (this.currentPage - 1) * this.pageSize + 1; + const endItem = Math.min(this.currentPage * this.pageSize, this.totalWords); + + const showingText = chrome.i18n.getMessage('showing_items') + ?.replace('{start}', String(startItem)) + .replace('{end}', String(endItem)) + .replace('{total}', String(this.totalWords)) + || `Showing ${startItem}-${endItem} of ${this.totalWords} words`; + + const pageInfoText = chrome.i18n.getMessage('page_info') + ?.replace('{current}', String(this.currentPage)) + .replace('{total}', String(totalPages)) + || `Page ${this.currentPage} of ${totalPages}`; + + const firstPageTitle = chrome.i18n.getMessage('first_page') || 'First page'; + const prevPageTitle = chrome.i18n.getMessage('previous_page') || 'Previous page'; + const nextPageTitle = chrome.i18n.getMessage('next_page') || 'Next page'; + const lastPageTitle = chrome.i18n.getMessage('last_page') || 'Last page'; + + paginationContainer.style.display = 'flex'; + paginationContainer.innerHTML = ` +
+ ${showingText} +
+
+ + +
+ ${pageInfoText} +
+ + +
+ `; + + this.setupPaginationEventListeners(); + } + + private setupPaginationEventListeners(): void { + document.getElementById('firstPageBtn')?.addEventListener('click', () => { + this.goToPage(1); + }); + + document.getElementById('prevPageBtn')?.addEventListener('click', () => { + this.goToPage(this.currentPage - 1); + }); + + document.getElementById('nextPageBtn')?.addEventListener('click', () => { + this.goToPage(this.currentPage + 1); + }); + + document.getElementById('lastPageBtn')?.addEventListener('click', () => { + const totalPages = Math.ceil(this.totalWords / this.pageSize); + this.goToPage(totalPages); + }); + } + + private goToPage(page: number): void { + const totalPages = Math.ceil(this.totalWords / this.pageSize); + if (page < 1 || page > totalPages) return; + + this.currentPage = page; + this.renderWords(); } private createWordItemHTML(word: HighlightWord, realIndex: number, isSelected: boolean): string {