Ver Fonte

Add navigation to last/first page

Wojtek há 1 ano atrás
pai
commit
a46e702536

+ 2 - 0
internal/locale/translations/de_DE.json

@@ -55,7 +55,9 @@
     "search.label": "Suche",
     "search.placeholder": "Suche...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Nächste",
+    "pagination.first": "First",
     "pagination.previous": "Vorherige",
     "entry.status.unread": "Ungelesen",
     "entry.status.read": "Gelesen",

+ 2 - 0
internal/locale/translations/el_EL.json

@@ -55,7 +55,9 @@
     "search.label": "Αναζήτηση",
     "search.placeholder": "Αναζήτηση...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Επόμενη",
+    "pagination.first": "First",
     "pagination.previous": "Προηγούμενη",
     "entry.status.unread": "Μη αναγνωσμένο",
     "entry.status.read": "Αναγνωσμένο",

+ 2 - 0
internal/locale/translations/en_US.json

@@ -55,7 +55,9 @@
     "search.label": "Search",
     "search.placeholder": "Search…",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Next",
+    "pagination.first": "First",
     "pagination.previous": "Previous",
     "entry.status.unread": "Unread",
     "entry.status.read": "Read",

+ 2 - 0
internal/locale/translations/es_ES.json

@@ -55,7 +55,9 @@
     "search.label": "Buscar",
     "search.placeholder": "Búsqueda...",
     "search.submit": "Search",
+    "pagination.last": "Ultimo",
     "pagination.next": "Siguiente",
+    "pagination.first": "Primero",
     "pagination.previous": "Anterior",
     "entry.status.unread": "No leído",
     "entry.status.read": "Leído",

+ 2 - 0
internal/locale/translations/fi_FI.json

@@ -55,7 +55,9 @@
     "search.label": "Haku",
     "search.placeholder": "Hae...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Seuraava",
+    "pagination.first": "First",
     "pagination.previous": "Edellinen",
     "entry.status.unread": "Lukematon",
     "entry.status.read": "Luettu",

+ 2 - 0
internal/locale/translations/fr_FR.json

@@ -55,7 +55,9 @@
     "search.label": "Recherche",
     "search.placeholder": "Recherche...",
     "search.submit": "Rechercher",
+    "pagination.last": "Last",
     "pagination.next": "Suivant",
+    "pagination.first": "First",
     "pagination.previous": "Précédent",
     "entry.status.unread": "Non lu",
     "entry.status.read": "Lu",

+ 2 - 0
internal/locale/translations/hi_IN.json

@@ -55,7 +55,9 @@
     "search.label": "खोजे",
     "search.placeholder": "खोजे...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "अगला",
+    "pagination.first": "First",
     "pagination.previous": "पिछला",
     "entry.status.unread": "अपठित",
     "entry.status.read": "पढ़े",

+ 2 - 0
internal/locale/translations/id_ID.json

@@ -56,6 +56,8 @@
     "search.placeholder": "Cari...",
     "search.submit": "Search",
     "pagination.next": "Berikutnya",
+    "pagination.last": "Last",
+    "pagination.first": "First",
     "pagination.previous": "Sebelumnya",
     "entry.status.unread": "Belum dibaca",
     "entry.status.read": "Telah dibaca",

+ 2 - 0
internal/locale/translations/it_IT.json

@@ -56,6 +56,8 @@
     "search.placeholder": "Cerca...",
     "search.submit": "Search",
     "pagination.next": "Successivo",
+    "pagination.last": "Last",
+    "pagination.first": "First",
     "pagination.previous": "Precedente",
     "entry.status.unread": "Da leggere",
     "entry.status.read": "Letto",

+ 2 - 0
internal/locale/translations/ja_JP.json

@@ -55,7 +55,9 @@
     "search.label": "検索",
     "search.placeholder": "…を検索",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "次",
+    "pagination.first": "First",
     "pagination.previous": "前",
     "entry.status.unread": "未読にする",
     "entry.status.read": "既読にする",

+ 2 - 0
internal/locale/translations/nl_NL.json

@@ -55,7 +55,9 @@
     "search.label": "Zoeken",
     "search.placeholder": "Zoeken...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Volgende",
+    "pagination.first": "First",
     "pagination.previous": "Vorige",
     "entry.status.unread": "Ongelezen",
     "entry.status.read": "Gelezen",

+ 2 - 0
internal/locale/translations/pl_PL.json

@@ -55,7 +55,9 @@
     "search.label": "Szukaj",
     "search.placeholder": "Szukaj...",
     "search.submit": "Search",
+    "pagination.last": "Ostatni",
     "pagination.next": "Następny",
+    "pagination.first": "Pierwszy",
     "pagination.previous": "Poprzedni",
     "entry.status.unread": "Nieprzeczytane",
     "entry.status.read": "Przeczytane",

+ 2 - 0
internal/locale/translations/pt_BR.json

@@ -55,7 +55,9 @@
     "search.label": "Buscar",
     "search.placeholder": "Buscar por...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Próximo",
+    "pagination.first": "First",
     "pagination.previous": "Anterior",
     "entry.status.unread": "Não lido",
     "entry.status.read": "Lido",

+ 2 - 0
internal/locale/translations/ru_RU.json

@@ -55,7 +55,9 @@
     "search.label": "Поиск",
     "search.placeholder": "Поиск…",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Следующая",
+    "pagination.first": "First",
     "pagination.previous": "Предыдущая",
     "entry.status.unread": "Не прочитано",
     "entry.status.read": "Прочитано",

+ 2 - 0
internal/locale/translations/tr_TR.json

@@ -485,7 +485,9 @@
   "page.users.title": "Kullanıcılar",
   "page.users.username": "Kullanıcı adı",
   "page.webauthn_rename.title": "Passkey'i Yeniden Adlandır",
+  "pagination.last": "Last",
   "pagination.next": "Sonraki",
+  "pagination.first": "First",
   "pagination.previous": "Önceki",
   "search.label": "Ara",
   "search.placeholder": "Ara...",

+ 2 - 0
internal/locale/translations/uk_UA.json

@@ -55,7 +55,9 @@
     "search.label": "Пошук",
     "search.placeholder": "Шукати...",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "Вперед",
+    "pagination.first": "First",
     "pagination.previous": "Назад",
     "entry.status.unread": "Непрочитане",
     "entry.status.read": "Прочитане",

+ 2 - 0
internal/locale/translations/zh_CN.json

@@ -55,7 +55,9 @@
     "search.label": "搜索",
     "search.placeholder": "搜索…",
     "search.submit": "Search",
+    "pagination.last": "Last",
     "pagination.next": "下一页",
+    "pagination.first": "First",
     "pagination.previous": "上一页",
     "entry.status.unread": "标为未读",
     "entry.status.read": "标为已读",

+ 2 - 0
internal/locale/translations/zh_TW.json

@@ -55,7 +55,9 @@
     "search.label": "搜尋",
     "search.placeholder": "搜尋…",
     "search.submit": "送出",
+    "pagination.last": "Last",
     "pagination.next": "下一頁",
+    "pagination.first": "First",
     "pagination.previous": "上一頁",
     "entry.status.unread": "標為未讀",
     "entry.status.read": "標為已讀",

+ 32 - 12
internal/template/templates/common/pagination.html

@@ -1,19 +1,39 @@
 {{ define "pagination" }}
 <div class="pagination">
-    <div class="pagination-prev {{ if not .ShowPrev }}disabled{{end}}">
-        {{ if .ShowPrev }}
-            <a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}{{ else }}{{ if .SearchQuery }}?q={{ .SearchQuery }}{{ end }}{{ end }}" data-page="previous" rel="prev">{{ t "pagination.previous" }}</a>
-        {{ else }}
-            {{ t "pagination.previous" }}
-        {{ end }}
+    <div class="pagination-backward">
+        <div class="pagination-first {{ if not .ShowFirst }}disabled{{end}}">
+            {{ if .ShowFirst }}
+                <a href="{{ .Route }}{{ if gt .FirstOffset 0 }}?offset={{ .FirstOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}{{ else }}{{ if .SearchQuery }}?q={{ .SearchQuery }}{{ end }}{{ end }}" data-page="first">{{ t "pagination.first" }}</a>
+            {{ else }}
+                {{ t "pagination.first" }}
+            {{ end }}
+        </div>
+
+        <div class="pagination-prev {{ if not .ShowPrev }}disabled{{end}}">
+            {{ if .ShowPrev }}
+                <a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}{{ else }}{{ if .SearchQuery }}?q={{ .SearchQuery }}{{ end }}{{ end }}" data-page="previous" rel="prev">{{ t "pagination.previous" }}</a>
+            {{ else }}
+                {{ t "pagination.previous" }}
+            {{ end }}
+        </div>
     </div>
 
-    <div class="pagination-next {{ if not .ShowNext }}disabled{{end}}">
-        {{ if .ShowNext }}
-            <a href="{{ .Route }}?offset={{ .NextOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}" data-page="next" rel="next">{{ t "pagination.next" }}</a>
-        {{ else }}
-            {{ t "pagination.next" }}
-        {{ end }}
+    <div class="pagination-forward">
+        <div class="pagination-next {{ if not .ShowNext }}disabled{{end}}">
+            {{ if .ShowNext }}
+                <a href="{{ .Route }}?offset={{ .NextOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}" data-page="next" rel="next">{{ t "pagination.next" }}</a>
+            {{ else }}
+                {{ t "pagination.next" }}
+            {{ end }}
+        </div>
+
+        <div class="pagination-last {{ if not .ShowLast }}disabled{{end}}">
+            {{ if .ShowLast }}
+                <a href="{{ .Route }}?offset={{ .LastOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}" data-page="last" >{{ t "pagination.last" }}</a>
+            {{ else }}
+                {{ t "pagination.last" }}
+            {{ end }}
+        </div>
     </div>
 </div>
 {{ end }}

+ 17 - 0
internal/ui/pagination.go

@@ -9,17 +9,30 @@ type pagination struct {
 	Offset       int
 	ItemsPerPage int
 	ShowNext     bool
+	ShowLast     bool
+	ShowFirst    bool
 	ShowPrev     bool
 	NextOffset   int
+	LastOffset   int
 	PrevOffset   int
+	FirstOffset  int
 	SearchQuery  string
 }
 
 func getPagination(route string, total, offset, nbItemsPerPage int) pagination {
 	nextOffset := 0
 	prevOffset := 0
+
+	firstOffset := 0
+	lastOffset := (total / nbItemsPerPage) * nbItemsPerPage
+	if lastOffset == total {
+		lastOffset -= nbItemsPerPage
+	}
+
 	showNext := (total - offset) > nbItemsPerPage
 	showPrev := offset > 0
+	showLast := showNext
+	showFirst := showPrev
 
 	if showNext {
 		nextOffset = offset + nbItemsPerPage
@@ -35,8 +48,12 @@ func getPagination(route string, total, offset, nbItemsPerPage int) pagination {
 		Offset:       offset,
 		ItemsPerPage: nbItemsPerPage,
 		ShowNext:     showNext,
+		ShowLast:     showLast,
 		NextOffset:   nextOffset,
+		LastOffset:   lastOffset,
 		ShowPrev:     showPrev,
+		ShowFirst:    showFirst,
 		PrevOffset:   prevOffset,
+		FirstOffset:  firstOffset,
 	}
 }

+ 24 - 4
internal/ui/static/css/common.css

@@ -710,6 +710,7 @@ template {
     font-size: 1.1em;
     display: flex;
     align-items: center;
+    justify-content: space-between;
 }
 
 .pagination-top {
@@ -732,21 +733,40 @@ template {
 }
 
 .pagination > div {
-    flex: 1;
+    display: flex;
+}
+
+.pagination > div.pagination-backward > div {
+    padding-right: 15px;
+}
+
+.pagination > div.pagination-forward > div {
+    padding-left: 15px;
 }
 
 .pagination-next {
     text-align: right;
 }
 
-.pagination-prev:before {
-    content: "« ";
+.pagination-next:after {
+    content: " ";
 }
 
-.pagination-next:after {
+.pagination-last {
+    text-align: right;
+}
+
+.pagination-last:after {
     content: " »";
 }
 
+.pagination-prev:before {
+    content: "‹ ";
+}
+.pagination-first:before {
+    content: "« ";
+}
+
 .pagination a {
     color: var(--pagination-link-color);
 }