From 389eefeb8ae2eaeff8c9bf80d0eb77c150ded46c Mon Sep 17 00:00:00 2001 From: perennial Date: Wed, 2 Oct 2024 01:16:45 +1000 Subject: [PATCH] Improve CreatePaginator with flexible margins and validation This commit improves the CreatePaginator function by adding support for customisable page margins and dropdown offsets. It also introduces input validation and error handling to ensure proper usage. --- assets/views/tag.jet.html | 2 +- server/template/templateFunctions.go | 95 ++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/assets/views/tag.jet.html b/assets/views/tag.jet.html index 575f7fc..db978ad 100644 --- a/assets/views/tag.jet.html +++ b/assets/views/tag.jet.html @@ -176,7 +176,7 @@ {{- url := unfinishedQuery(.QueriesC, "page") }} - {{- paginationData := createPaginator(url, "#checkpoint", .Page, -1) }} + {{- paginationData := createPaginator(url, "#checkpoint", .Page, -1, 1, 5) }} {{- yield pagination(data=paginationData) }} {{- end }} diff --git a/server/template/templateFunctions.go b/server/template/templateFunctions.go index fb117c6..091909c 100644 --- a/server/template/templateFunctions.go +++ b/server/template/templateFunctions.go @@ -160,42 +160,81 @@ func ParseTimeCustomFormat(date time.Time, format string) string { return date.Format(format) } -// CreatePaginator generates pagination data based on the current page and maximum number of pages -func CreatePaginator(base, ending string, current_page, max_page int) PaginationData { +// CreatePaginator generates pagination data based on the current page and maximum number of pages. +// It returns a PaginationData struct containing all necessary information for rendering pagination controls. +// +// Parameters: +// - base: The base part of the pagination URL +// - ending: The ending part of the pagination URL +// - current_page: The current page being displayed +// - max_page: Maximum number of pages (-1 if unknown) +// - page_margin: Number of pages to display before and after the current page +// - dropdown_offset: Number of pages to include in dropdown before and after current page +func CreatePaginator(base, ending string, current_page, max_page, page_margin, dropdown_offset int) (PaginationData, error) { + // Validate input parameters + if current_page < 1 { + return PaginationData{}, fmt.Errorf("current_page must be greater than or equal to 1, got %d", current_page) + } + if max_page != -1 && max_page < current_page { + return PaginationData{}, fmt.Errorf("max_page (%d) must be greater than or equal to current_page (%d) when specified", max_page, current_page) + } + if page_margin < 0 { + return PaginationData{}, fmt.Errorf("page_margin must be non-negative, got %d", page_margin) + } + if dropdown_offset < 0 { + return PaginationData{}, fmt.Errorf("dropdown_offset must be non-negative, got %d", dropdown_offset) + } + + hasMaxPage := max_page != -1 + pageUrl := func(page int) string { return fmt.Sprintf(`%s%d%s`, base, page, ending) } - // Number of pages to show on each side of the current page - // - // NOTE: values higher than 1 can cause issues on small devices where the pagination element is too wide - const peek = 1 - hasMaxPage := max_page != -1 + // Helper function to generate a range of pages + generatePageRange := func(start, end int) []PageInfo { + if start > end { + return []PageInfo{} + } + pages := make([]PageInfo, 0, end-start+1) + for i := start; i <= end; i++ { + pages = append(pages, PageInfo{Number: i, URL: pageUrl(i)}) + } + return pages + } // Calculate the range of pages to display - start := max(1, current_page-peek) - end := current_page + peek + start := max(1, current_page-page_margin) + end := current_page + page_margin if hasMaxPage { end = min(max_page, end) } // Generate page information for the range - pages := make([]PageInfo, 0, end-start+1) - for i := start; i <= end; i++ { - pages = append(pages, PageInfo{Number: i, URL: pageUrl(i)}) + pages := generatePageRange(start, end) + + var lastPage int + if len(pages) > 0 { + lastPage = pages[len(pages)-1].Number + } else { + lastPage = current_page } - lastPage := pages[len(pages)-1].Number - - // Generate dropdown pages (previous 5, current, next 5) - dropdownStart := max(1, current_page-5) - dropdownEnd := current_page + 5 + // Generate dropdown pages + dropdownStart := max(1, current_page-dropdown_offset) + dropdownEnd := current_page + dropdown_offset if hasMaxPage { dropdownEnd = min(max_page, dropdownEnd) } - dropdownPages := make([]PageInfo, 0, dropdownEnd-dropdownStart+1) - for i := dropdownStart; i <= dropdownEnd; i++ { - dropdownPages = append(dropdownPages, PageInfo{Number: i, URL: pageUrl(i)}) + dropdownPages := generatePageRange(dropdownStart, dropdownEnd) + + // Calculate previous and next URLs + var previousURL, nextURL string + if current_page > 1 { + previousURL = pageUrl(current_page - 1) + } + if !hasMaxPage || current_page < max_page { + nextURL = pageUrl(current_page + 1) } // Create and return the PaginationData struct @@ -205,14 +244,14 @@ func CreatePaginator(base, ending string, current_page, max_page int) Pagination Pages: pages, HasPrevious: current_page > 1, HasNext: !hasMaxPage || current_page < max_page, - PreviousURL: pageUrl(current_page - 1), - NextURL: pageUrl(current_page + 1), + PreviousURL: previousURL, + NextURL: nextURL, FirstURL: pageUrl(1), LastURL: pageUrl(max_page), HasMaxPage: hasMaxPage, LastPage: lastPage, DropdownPages: dropdownPages, - } + }, nil } // GetNovelGenre returns the genre name for a given genre ID @@ -316,8 +355,14 @@ func GetTemplateFunctions() map[string]any { "parseTimeCustomFormat": func(date time.Time, format string) string { return ParseTimeCustomFormat(date, format) }, - "createPaginator": func(base, ending string, current_page, max_page int) PaginationData { - return CreatePaginator(base, ending, current_page, max_page) + "createPaginator": func(base, ending string, current_page, max_page, page_margin, dropdown_offset int) PaginationData { + paginationData, err := CreatePaginator(base, ending, current_page, max_page, page_margin, dropdown_offset) + if err != nil { + fmt.Printf("Error creating paginator: %v", err) + // Return an empty PaginationData in case of error + return PaginationData{} + } + return paginationData }, "joinArtworkIds": func(artworks []core.ArtworkBrief) string { ids := []string{}