From 15283aac3746f39833639afc79c6e1f5d09ca184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Eduardo=20Jer=C3=A9z=20Gir=C3=B3n?= Date: Sat, 10 Aug 2024 22:12:38 -0600 Subject: [PATCH] Update pagination logic to support querying and paginating icons --- internal/icons/paginate_icons.go | 36 ++++++++++++++ internal/web/page/index_editor_icon.go | 6 ++- internal/web/page/search_icon.go | 68 ++++++++++++++++---------- 3 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 internal/icons/paginate_icons.go diff --git a/internal/icons/paginate_icons.go b/internal/icons/paginate_icons.go new file mode 100644 index 0000000..cfe8b29 --- /dev/null +++ b/internal/icons/paginate_icons.go @@ -0,0 +1,36 @@ +package icons + +// PaginateIcons returns a slice of icons for a given page, size and query. +// The query is used to filter the icons. +// +// The second return value is a boolean indicating if there are more icons to +// be paginated. +// +// The third return value is the next page of icons. +func PaginateIcons(page, size int, query string) ([]Icon, bool, int) { + foundIcons := []Icon{} + + if query == "" { + foundIcons = GetAllIcons() + } else { + foundIcons = SearchIcons(query) + } + + start := (page - 1) * size + if start >= len(foundIcons) { + return []Icon{}, false, 1 + } + + end := start + size + if end > len(foundIcons) { + end = len(foundIcons) + } + + hasNextPage := end < len(foundIcons) + nextPage := page + if hasNextPage { + nextPage = page + 1 + } + + return foundIcons[start:end], hasNextPage, nextPage +} diff --git a/internal/web/page/index_editor_icon.go b/internal/web/page/index_editor_icon.go index 1c18ed5..32d8e99 100644 --- a/internal/web/page/index_editor_icon.go +++ b/internal/web/page/index_editor_icon.go @@ -40,11 +40,15 @@ func indexEditorIconPicker() gomponents.Node { html.Div( html.ID("icon-picker-results"), + html.Class("grid grid-cols-7 gap-2"), htmx.HxGet("/icons"), htmx.HxTrigger("intersect once"), htmx.HxIndicator("#icon-picker-indicator"), - component.SpinnerContainerLg(), + html.Div( + html.Class("col-span-7"), + component.SpinnerContainerLg(), + ), ), ), }, diff --git a/internal/web/page/search_icon.go b/internal/web/page/search_icon.go index 2b67003..3e1f2b1 100644 --- a/internal/web/page/search_icon.go +++ b/internal/web/page/search_icon.go @@ -1,51 +1,65 @@ package page import ( + "fmt" "net/http" + "strconv" "github.com/eduardolat/generate-logo-online/internal/icons" "github.com/eduardolat/generate-logo-online/internal/util/echoutil" "github.com/eduardolat/generate-logo-online/internal/web/alpine" + "github.com/eduardolat/generate-logo-online/internal/web/component" + "github.com/eduardolat/generate-logo-online/internal/web/htmx" "github.com/labstack/echo/v4" "github.com/maragudk/gomponents" "github.com/maragudk/gomponents/html" ) func (h *handlers) searchIconHandler(c echo.Context) error { + const pageSize = 7 * 8 + query := c.QueryParam("q") - if query == "" { - allIcons := icons.GetAllIcons() - return echoutil.RenderGomponent( - c, http.StatusOK, - searchIconResults(allIcons), - ) + page, err := strconv.Atoi(c.QueryParam("p")) + if err != nil { + page = 1 } - foundIcons := icons.SearchIcons(query) + foundIcons, hasNextPage, nextPage := icons.PaginateIcons(page, pageSize, query) return echoutil.RenderGomponent( c, http.StatusOK, - searchIconResults(foundIcons), + searchIconResults(foundIcons, query, hasNextPage, nextPage), ) } -func searchIconResults(foundIcons []icons.Icon) gomponents.Node { - return html.Div( - html.Class("grid grid-cols-7 gap-2"), - gomponents.Group( - gomponents.Map( - foundIcons, - func(icon icons.Icon) gomponents.Node { - return html.Div( - html.Class("tooltip"), - html.Button( - html.Type("button"), - html.Class("btn btn-outline btn-square btn-lg w-full"), - alpine.XOn("click", `setOriginalSVG('`+icon.SVG+`')`), - html.Title(icon.Name), - icon.Icon(html.Class("size-8")), - ), - ) +func searchIconResults( + foundIcons []icons.Icon, query string, hasNextPage bool, nextPage int, +) gomponents.Node { + iconsLen := len(foundIcons) + + buttons := []gomponents.Node{} + for i, icon := range foundIcons { + buttons = append(buttons, html.Div( + html.Button( + html.Type("button"), + html.Class("btn btn-outline btn-square btn-lg w-full"), + alpine.XOn("click", `setOriginalSVG('`+icon.SVG+`')`), + html.Title(icon.Name), + icon.Icon(html.Class("size-8")), + ), + + gomponents.If( + i == iconsLen-1 && hasNextPage, + gomponents.Group([]gomponents.Node{ + htmx.HxGet(fmt.Sprintf( + "/icons?q=%s&p=%d", query, nextPage, + )), + htmx.HxTrigger("intersect once"), + htmx.HxSwap("afterend"), + htmx.HxIndicator("#icon-picker-indicator"), }), - ), - ) + ), + )) + } + + return component.RenderableGroup(buttons) }