110110 <app-icon variant =" chevron_left" size =" 22" no-margin />
111111 </button >
112112
113- <button
114- v-for =" page in totalPages"
115- :key =" page"
116- type =" button"
117- :class =" { 'project-detail-task-pagination-active': page === currentPage }"
118- :aria-current =" page === currentPage ? 'page' : undefined"
119- @click =" currentPage = page"
120- >
121- {{ page }}
122- </button >
113+ <template v-for =" item in visiblePaginationItems " :key =" item .key " >
114+ <span
115+ v-if =" item.type === 'ellipsis'"
116+ class =" project-detail-task-pagination-ellipsis"
117+ aria-hidden =" true"
118+ >
119+ ...
120+ </span >
121+
122+ <button
123+ v-else
124+ type =" button"
125+ :class =" { 'project-detail-task-pagination-active': item.page === currentPage }"
126+ :aria-current =" item.page === currentPage ? 'page' : undefined"
127+ @click =" currentPage = item.page"
128+ >
129+ {{ item.page }}
130+ </button >
131+ </template >
123132
124133 <button
125134 type =" button"
@@ -151,6 +160,9 @@ import type { WorkspaceProjectTaskListItem, WorkspaceProjectTaskStatus } from '~
151160
152161type TaskSortOption = ' latest' | ' oldest' | ' task_asc' | ' task_desc' ;
153162type TaskStatusFilter = WorkspaceProjectTaskStatus | ' all' ;
163+ type PaginationItem =
164+ | { type: ' page' ; key: string ; page: number }
165+ | { type: ' ellipsis' ; key: string };
154166
155167interface SelectOption {
156168 label: string ;
@@ -174,8 +186,10 @@ const emit = defineEmits<{
174186 ' unlock-task' : [taskNumber : number ];
175187}>();
176188
177- /** Number of tasks shown per page. Change this if you want larger or smaller pages. */
178- const pageSize = 5 ;
189+ /** Keep the page short enough to fit the panel while matching the requested 6-row layout. */
190+ const pageSize = 6 ;
191+ /** Limit how many numbered pagination buttons we render before collapsing with ellipses. */
192+ const maxVisiblePaginationButtons = 7 ;
179193const searchQuery = ref (' ' );
180194const selectedStatus = ref <TaskStatusFilter >(' all' );
181195const sortBy = ref <TaskSortOption >(' latest' );
@@ -259,6 +273,55 @@ const paginationSummary = computed(() => {
259273 return ` Showing ${start } to ${end } of ${sortedTasks .value .length } entries ` ;
260274});
261275
276+ /**
277+ * Compress long pagination runs so the footer stays readable:
278+ * 1 ... 7 8 9 ... 17
279+ * instead of rendering every page button in a single line.
280+ */
281+ const visiblePaginationItems = computed <PaginationItem []>(() => {
282+ const pages = totalPages .value ;
283+
284+ if (pages <= maxVisiblePaginationButtons ) {
285+ return Array .from ({ length: pages }, (_ , index ) => ({
286+ type: ' page' as const ,
287+ key: ` page-${index + 1 } ` ,
288+ page: index + 1 ,
289+ }));
290+ }
291+
292+ const firstPage = 1 ;
293+ const lastPage = pages ;
294+ const current = currentPage .value ;
295+ const interiorSlots = maxVisiblePaginationButtons - 2 ;
296+ let windowStart = Math .max (firstPage + 1 , current - Math .floor (interiorSlots / 2 ));
297+ let windowEnd = windowStart + interiorSlots - 1 ;
298+
299+ if (windowEnd >= lastPage ) {
300+ windowEnd = lastPage - 1 ;
301+ windowStart = windowEnd - interiorSlots + 1 ;
302+ }
303+
304+ const items: PaginationItem [] = [
305+ { type: ' page' , key: ` page-${firstPage } ` , page: firstPage },
306+ ];
307+
308+ if (windowStart > firstPage + 1 ) {
309+ items .push ({ type: ' ellipsis' , key: ' ellipsis-start' });
310+ }
311+
312+ for (let page = windowStart ; page <= windowEnd ; page += 1 ) {
313+ items .push ({ type: ' page' , key: ` page-${page } ` , page });
314+ }
315+
316+ if (windowEnd < lastPage - 1 ) {
317+ items .push ({ type: ' ellipsis' , key: ' ellipsis-end' });
318+ }
319+
320+ items .push ({ type: ' page' , key: ` page-${lastPage } ` , page: lastPage });
321+
322+ return items ;
323+ });
324+
262325/**
263326 * Reset to page 1 whenever the user changes the search, status filter, or sort.
264327 * Without this, the user could be on page 3 and change the filter to show only 2 tasks,
@@ -354,11 +417,12 @@ function formatTaskStatus(status: WorkspaceProjectTaskStatus) {
354417}
355418
356419.project-detail-task-list-wrap {
420+ position : relative ;
357421 display : grid ;
358422 gap : 0 ;
359423 border : 1px solid rgba ($text-navy , 0.08 );
360424 border-radius : 0.85rem ;
361- overflow : hidden ;
425+ overflow : visible ;
362426}
363427
364428.project-detail-task-list-header ,
@@ -380,6 +444,7 @@ function formatTaskStatus(status: WorkspaceProjectTaskStatus) {
380444 margin : 0 ;
381445 padding : 0 ;
382446 list-style : none ;
447+ overflow : visible ;
383448}
384449
385450.project-detail-task-item {
@@ -485,22 +550,28 @@ function formatTaskStatus(status: WorkspaceProjectTaskStatus) {
485550 align-items : center ;
486551 justify-content : space-between ;
487552 gap : 1rem ;
553+ flex-wrap : wrap ;
488554 padding-top : 1rem ;
489555}
490556
491557.project-detail-task-footer p {
492558 margin : 0 ;
493559 color : #5a607b ;
494560 font-size : 0.95rem ;
561+ flex : 0 1 auto ;
495562}
496563
497564.project-detail-task-pagination {
498565 display : flex ;
499566 align-items : center ;
500567 gap : 0.25rem ;
568+ flex : 1 1 auto ;
569+ flex-wrap : wrap ;
570+ justify-content : flex-end ;
501571}
502572
503573.project-detail-task-pagination button {
574+ min-width : 2.15rem ;
504575 width : 2.15rem ;
505576 height : 2.15rem ;
506577 display : inline-flex ;
@@ -517,6 +588,15 @@ function formatTaskStatus(status: WorkspaceProjectTaskStatus) {
517588 background : $primary !important ;
518589}
519590
591+ .project-detail-task-pagination-ellipsis {
592+ min-width : 1.5rem ;
593+ display : inline-flex ;
594+ align-items : center ;
595+ justify-content : center ;
596+ color : #7b819c ;
597+ font-size : 1rem ;
598+ }
599+
520600.project-detail-task-pagination button :disabled {
521601 opacity : 0.45 ;
522602}
@@ -563,6 +643,10 @@ function formatTaskStatus(status: WorkspaceProjectTaskStatus) {
563643 flex-direction : column ;
564644 align-items : flex-start ;
565645 }
646+
647+ .project-detail-task-pagination {
648+ justify-content : flex-start ;
649+ }
566650}
567651
568652@container (max-width: 420px) {
@@ -580,5 +664,9 @@ function formatTaskStatus(status: WorkspaceProjectTaskStatus) {
580664 flex-direction : column ;
581665 align-items : flex-start ;
582666 }
667+
668+ .project-detail-task-pagination {
669+ justify-content : flex-start ;
670+ }
583671}
584672 </style >
0 commit comments