pagination: implemented seek page feature
Some checks failed
/ push (push) Has been cancelled

This commit is contained in:
Tigor Hutasuhut 2024-05-12 22:45:41 +07:00
parent 3f3bc72f30
commit fdfad2f9e1
3 changed files with 130 additions and 14 deletions

View file

@ -71,8 +71,8 @@ templ ImageCard(data *models.Image, opts ImageCardOption) {
<p class="text-xs text-end">{ formatByteSize(data.ImageSize) }</p> <p class="text-xs text-end">{ formatByteSize(data.ImageSize) }</p>
</div> </div>
if data.R.Device != nil && !opts.Has(HideDevice) { if data.R.Device != nil && !opts.Has(HideDevice) {
<a href={ templ.URL(fmt.Sprintf("/devices/details/%s", data.R.Device.Slug)) }> <a class="my-4" href={ templ.URL(fmt.Sprintf("/devices/details/%s", data.R.Device.Slug)) }>
<div class="divider text-accent underline">{ data.R.Device.Name }</div> <div class="divider text-accent underline text-wrap text-center">{ data.R.Device.Name }</div>
</a> </a>
} }
</div> </div>

View file

@ -4,28 +4,131 @@ import "github.com/tigorlazuardi/redmage/views"
import "strconv" import "strconv"
import "github.com/tigorlazuardi/redmage/views/utils" import "github.com/tigorlazuardi/redmage/views/utils"
import "strings" import "strings"
import "fmt"
type PaginationData struct { type PaginationData struct {
Offset int64 Offset int64
Limit int64 Limit int64
BaseURL string BaseURL string
Total int64 Total int64
Classes []string Classes []string
UnhideRange int
}
type pageStatus int
const (
pageStatusShow = iota
pageStatusHidden
pageStatusDot
)
func (pgdata PaginationData) GetUnhideRange() int {
if pgdata.UnhideRange < 1 {
return 2
}
return pgdata.UnhideRange
}
func (pgdata PaginationData) getPageStatus(page int) pageStatus {
if page < 2 {
return pageStatusShow
}
lastPage := (pgdata.Total / max(pgdata.Limit, 1)) + 1
if page == int(lastPage) {
return pageStatusShow
}
current := pgdata.GetCurrentPage()
unhideRange := pgdata.GetUnhideRange()
if page == current-unhideRange-1 {
return pageStatusDot
}
if page == current+unhideRange+1 {
return pageStatusDot
}
if page >= current-unhideRange && page <= current+unhideRange {
return pageStatusShow
}
return pageStatusHidden
}
func (pgdata PaginationData) GetCurrentPage() int {
return int(pgdata.Offset/pgdata.Limit) + 1
}
func (pgdata PaginationData) GetTotalPage() int {
return int(pgdata.Total/max(pgdata.Limit, 1)) + 1
} }
templ Pagination(c *views.Context, data PaginationData) { templ Pagination(c *views.Context, data PaginationData) {
if data.Total > data.Limit { if data.Total > data.Limit {
<div class="join"> <div
for i, count := 1, int64(0); count < data.Total; i, count = i+1, count+data.Limit { x-data={ fmt.Sprintf(`{
<a page: %d,
href={ c.URLWithExtraQuery(data.BaseURL, "offset", strconv.FormatInt(count, 10)) } qs: %s,
class={ utils.CXX( max: %d,
get vals() { return JSON.stringify({...this.qs, offset: (Math.max(1, Math.min(this.page, this.max))-1) * (this.qs.limit || 25)}) }
}`, data.GetCurrentPage(), c.JSONQuery(), data.GetTotalPage()) }
>
<div class="join">
for i, count := 1, int64(0); count < data.Total; i, count = i+1, count+data.Limit {
if data.GetTotalPage() <= 10 || data.getPageStatus(i) == pageStatusShow {
<a
href={ c.URLWithExtraQuery(data.BaseURL, "offset", strconv.FormatInt(count, 10)) }
class={ utils.CXX(
"join-item btn no-underline", true, "join-item btn no-underline", true,
"btn-active", data.Offset <= count && data.Offset > count - data.Limit , "btn-active", data.Offset <= count && data.Offset > count - data.Limit ,
strings.Join(data.Classes, " "), len(data.Classes) > 0, strings.Join(data.Classes, " "), len(data.Classes) > 0,
) } ) }
>{ strconv.Itoa(i) }</a> >{ strconv.Itoa(i) }</a>
} } else if data.getPageStatus(i) == pageStatusDot {
<span @click="$refs.dialog.showModal()" class="join-item btn">...</span>
}
}
</div>
<dialog
onclick="if (event.target === this) this.close()"
x-ref="dialog"
class="modal"
>
<div class="modal-box">
<h2 class="my-2">Seek Page</h2>
<div
id="page-seeker"
class="join flex"
>
<input
type="number"
value={ strconv.Itoa(data.GetCurrentPage()) }
min="1"
max={ strconv.Itoa(data.GetTotalPage()) }
placeholder="Seek Page"
class="input input-bordered join-item w-full"
x-model="page"
hx-get={ c.Request.URL.Path }
hx-target="main"
hx-select="main"
hx-swap="outerHTML"
hx-push-url="true"
hx-trigger="custom"
@keyup.enter="htmx.trigger($el, 'custom')"
:hx-vals="vals"
/>
<button
hx-get={ c.Request.URL.Path }
hx-target="main"
hx-select="main"
hx-swap="outerHTML"
hx-push-url="true"
:hx-vals="vals"
hx-trigger="click"
type="button"
class="btn btn-primary join-item"
>Go</button>
</div>
</div>
</dialog>
</div> </div>
} }
} }

View file

@ -1,6 +1,7 @@
package views package views
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
@ -35,6 +36,18 @@ func (c *Context) URLWithExtraQuery(baseUrl string, extraKeyValues ...string) te
return templ.SafeURL(fmt.Sprintf("%s?%s", baseUrl, query.Encode())) return templ.SafeURL(fmt.Sprintf("%s?%s", baseUrl, query.Encode()))
} }
func (c *Context) JSONQuery() string {
m := make(map[string]string, len(c.Query))
for k := range c.Query {
v := c.Query.Get(k)
if v != "" {
m[k] = v
}
}
v, _ := json.Marshal(m)
return string(v)
}
func NewContext(config *config.Config, request *http.Request) *Context { func NewContext(config *config.Config, request *http.Request) *Context {
return &Context{ return &Context{
Config: config, Config: config,