Use binding mechanism for the Pagination component

- Rename PaginationEvent to PaginateEvent
- Add PaginationBinder interface
This commit is contained in:
David Vogel 2023-05-29 14:23:37 +02:00
parent 37d42e9a94
commit c149bbfc06
6 changed files with 93 additions and 47 deletions

View File

@ -2,22 +2,22 @@ package navigation
import "github.com/vugu/vugu"
type PaginationEvent struct {
type PaginateEvent struct {
vugu.DOMEvent
Page int
PaginationInfo // The current state of the pagination component.
}
// PaginationHandler is the interface for things that can handle PaginationEvent.
type PaginationHandler interface {
PaginationHandle(event PaginationEvent)
type PaginateHandler interface {
PaginateHandle(event PaginateEvent)
}
// PaginationFunc implements PaginationHandler as a function.
type PaginationFunc func(event PaginationEvent)
// PaginateFunc implements PaginateHandler as a function.
type PaginateFunc func(event PaginateEvent)
// PaginationHandle implements the PaginationHandler interface.
func (f PaginationFunc) PaginationHandle(event PaginationEvent) { f(event) }
// PaginateHandle implements the PaginateHandler interface.
func (f PaginateFunc) PaginateHandle(event PaginateEvent) { f(event) }
// assert PaginationFunc implements PaginationHandler.
var _ PaginationHandler = PaginationFunc(nil)
// assert PaginateFunc implements PaginateHandler.
var _ PaginateHandler = PaginateFunc(nil)

View File

@ -0,0 +1,26 @@
package navigation
// PaginationBinder is an interface that everything that every type that wants to bind to an pagination component must implement.
type PaginationBinder interface {
Page() int // Page returns the current page, as a 1-based index. A value of 0 means there is no active page.
Pages() int // Pages returns the total number of pages.
SetPage(page int) // SetPage changes the current page as a 1-based index. A value of 0 means there is no active page.
}
// PaginationInfo implements the PaginationBinder interface and represents the current state of a pagination component.
type PaginationInfo struct {
CurrentPage int // The current page as a 1-based index. A value of 0 means there is no current page.
TotalPages int // The number of total pages.
}
func (p *PaginationInfo) Page() int {
return p.CurrentPage
}
func (p *PaginationInfo) Pages() int {
return p.TotalPages
}
func (p *PaginationInfo) SetPage(page int) {
p.CurrentPage = page
}

View File

@ -10,10 +10,9 @@ type Pagination struct {
buttons []paginationButtonInfo
Page int `vugu:"data"`
Pages int `vugu:"data"`
Bind PaginationBinder
Pagination PaginationHandler // External handler that is called upon an event.
Paginate PaginateHandler // External handler that is called upon an event.
}
type paginationButtonInfo struct {
@ -21,32 +20,55 @@ type paginationButtonInfo struct {
Class string
}
func (c *Pagination) Page() int {
if c.Bind == nil {
return 0
}
return c.Bind.Page()
}
func (c *Pagination) Pages() int {
if c.Bind == nil {
return 0
}
return c.Bind.Pages()
}
func (c *Pagination) handleClickPrev(event input.ClickEvent) {
c.handleClickPage(event, c.Page-1)
c.handleClickPage(event, c.Page()-1)
}
func (c *Pagination) handleClickNext(event input.ClickEvent) {
c.handleClickPage(event, c.Page+1)
c.handleClickPage(event, c.Page()+1)
}
func (c *Pagination) handleClickPage(event input.ClickEvent, page int) {
if page <= 0 || page > c.Pages {
pages := c.Pages()
if page <= 0 || page > pages {
return
}
if c.Pagination != nil {
c.Pagination.PaginationHandle(PaginationEvent{
if c.Bind != nil {
c.Bind.SetPage(page)
}
if c.Paginate != nil {
c.Paginate.PaginateHandle(PaginateEvent{
DOMEvent: event,
Page: page,
PaginationInfo: PaginationInfo{
CurrentPage: page,
TotalPages: pages,
},
})
}
}
func (c *Pagination) Init(ctx vugu.InitCtx) {
c.Pages = 10
}
func (c *Pagination) Compute(ctx vugu.ComputeCtx) {
page, pages := c.Page(), c.Pages()
// Number of buttons around the current page.
// Excluding first and last page.
const AroundButtons = 2
@ -60,15 +82,15 @@ func (c *Pagination) Compute(ctx vugu.ComputeCtx) {
// Prepare buttons.
buttons := [TotalButtons]paginationButtonInfo{
0: {Page: 1},
TotalButtons - 1: {Page: c.Pages},
TotalButtons - 1: {Page: pages},
}
startPage := 2
if startPage < c.Page-AroundButtons {
startPage = c.Page - AroundButtons
if startPage < page-AroundButtons {
startPage = page - AroundButtons
}
if startPage > c.Pages-DynamicButtons {
startPage = c.Pages - DynamicButtons
if startPage > pages-DynamicButtons {
startPage = pages - DynamicButtons
}
for i := 0; i < DynamicButtons; i++ {
@ -79,13 +101,13 @@ func (c *Pagination) Compute(ctx vugu.ComputeCtx) {
// pageCounter is used to check if pages are strictly monotonically increasing.
buttonsFiltered := make([]paginationButtonInfo, 0, len(buttons))
pageCounter := 0
for _, page := range buttons {
if pageCounter < page.Page && page.Page <= c.Pages {
pageCounter = page.Page
if page.Page == c.Page {
page.Class = "d3c-button-highlight"
for _, pageInfo := range buttons {
if pageCounter < pageInfo.Page && pageInfo.Page <= pages {
pageCounter = pageInfo.Page
if pageInfo.Page == page {
pageInfo.Class = "d3c-button-highlight"
}
buttonsFiltered = append(buttonsFiltered, page)
buttonsFiltered = append(buttonsFiltered, pageInfo)
}
}

View File

@ -8,15 +8,14 @@ import (
)
type PageLayout struct {
Page int `vugu:"data"`
Pages int `vugu:"data"`
Pagination navigation.PaginationInfo `vugu:"data"`
}
func (c *PageLayout) Init(ctx vugu.InitCtx) {
c.Pages = 10
c.Pagination.CurrentPage = 2
c.Pagination.TotalPages = 10
}
func (c *PageLayout) handlePagination(event navigation.PaginationEvent) {
log.Printf("Some page %v", event.Page)
c.Page = event.Page
func (c *PageLayout) handlePagination(event navigation.PaginateEvent) {
log.Printf("Current pagination info: %v", event.PaginationInfo)
}

View File

@ -3,7 +3,7 @@
<h1>Layout</h1>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
<h2>Items</h2>
<navigation:Pagination :Page="c.Page" :Pages="c.Pages" @Pagination="c.handlePagination(event)"></navigation:Pagination>
<navigation:Pagination :Bind="&c.Pagination" @Paginate="c.handlePagination(event)"></navigation:Pagination>
<main:ComponentAddress Name="Max Mustermann" Street="Musterstraße 45" PostalCode="12345" City="Musterstadt" Country="Germany"></main:ComponentAddress>
</layout:Container>
</div>

View File

@ -28,7 +28,7 @@ func (c *PageLayout) Build(vgin *vugu.BuildIn) (vgout *vugu.BuildOut) {
vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n\t"}
vgparent.AppendChild(vgn)
{
vgcompKey := vugu.MakeCompKey(0x3467920B61750485^vgin.CurrentPositionHash(), vgiterkey)
vgcompKey := vugu.MakeCompKey(0x60B66E06F722C68^vgin.CurrentPositionHash(), vgiterkey)
// ask BuildEnv for prior instance of this specific component
vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*layout.Container)
if vgcomp == nil {
@ -62,7 +62,7 @@ func (c *PageLayout) Build(vgin *vugu.BuildIn) (vgout *vugu.BuildOut) {
vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n\t\t"}
vgparent.AppendChild(vgn)
{
vgcompKey := vugu.MakeCompKey(0x6A61FD21D9782C05^vgin.CurrentPositionHash(), vgiterkey)
vgcompKey := vugu.MakeCompKey(0xD22F5BB58705C5DF^vgin.CurrentPositionHash(), vgiterkey)
// ask BuildEnv for prior instance of this specific component
vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*navigation.Pagination)
if vgcomp == nil {
@ -71,9 +71,8 @@ func (c *PageLayout) Build(vgin *vugu.BuildIn) (vgout *vugu.BuildOut) {
vgin.BuildEnv.WireComponent(vgcomp)
}
vgin.BuildEnv.UseComponent(vgcompKey, vgcomp) // ensure we can use this in the cache next time around
vgcomp.Page = c.Page
vgcomp.Pages = c.Pages
vgcomp.Pagination = navigation.PaginationFunc(func(event navigation.PaginationEvent) { c.handlePagination(event) })
vgcomp.Bind = &c.Pagination
vgcomp.Paginate = navigation.PaginateFunc(func(event navigation.PaginateEvent) { c.handlePagination(event) })
vgout.Components = append(vgout.Components, vgcomp)
vgn = &vugu.VGNode{Component: vgcomp}
vgparent.AppendChild(vgn)
@ -81,7 +80,7 @@ func (c *PageLayout) Build(vgin *vugu.BuildIn) (vgout *vugu.BuildOut) {
vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n\t\t"}
vgparent.AppendChild(vgn)
{
vgcompKey := vugu.MakeCompKey(0xD67C1752436E9AB1^vgin.CurrentPositionHash(), vgiterkey)
vgcompKey := vugu.MakeCompKey(0xE6379FC706A49E07^vgin.CurrentPositionHash(), vgiterkey)
// ask BuildEnv for prior instance of this specific component
vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*ComponentAddress)
if vgcomp == nil {