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" import "github.com/vugu/vugu"
type PaginationEvent struct { type PaginateEvent struct {
vugu.DOMEvent vugu.DOMEvent
Page int PaginationInfo // The current state of the pagination component.
} }
// PaginationHandler is the interface for things that can handle PaginationEvent. // PaginationHandler is the interface for things that can handle PaginationEvent.
type PaginationHandler interface { type PaginateHandler interface {
PaginationHandle(event PaginationEvent) PaginateHandle(event PaginateEvent)
} }
// PaginationFunc implements PaginationHandler as a function. // PaginateFunc implements PaginateHandler as a function.
type PaginationFunc func(event PaginationEvent) type PaginateFunc func(event PaginateEvent)
// PaginationHandle implements the PaginationHandler interface. // PaginateHandle implements the PaginateHandler interface.
func (f PaginationFunc) PaginationHandle(event PaginationEvent) { f(event) } func (f PaginateFunc) PaginateHandle(event PaginateEvent) { f(event) }
// assert PaginationFunc implements PaginationHandler. // assert PaginateFunc implements PaginateHandler.
var _ PaginationHandler = PaginationFunc(nil) 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 buttons []paginationButtonInfo
Page int `vugu:"data"` Bind PaginationBinder
Pages int `vugu:"data"`
Pagination PaginationHandler // External handler that is called upon an event. Paginate PaginateHandler // External handler that is called upon an event.
} }
type paginationButtonInfo struct { type paginationButtonInfo struct {
@ -21,32 +20,55 @@ type paginationButtonInfo struct {
Class string 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) { 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) { 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) { func (c *Pagination) handleClickPage(event input.ClickEvent, page int) {
if page <= 0 || page > c.Pages { pages := c.Pages()
if page <= 0 || page > pages {
return return
} }
if c.Pagination != nil { if c.Bind != nil {
c.Pagination.PaginationHandle(PaginationEvent{ c.Bind.SetPage(page)
}
if c.Paginate != nil {
c.Paginate.PaginateHandle(PaginateEvent{
DOMEvent: event, 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) { func (c *Pagination) Compute(ctx vugu.ComputeCtx) {
page, pages := c.Page(), c.Pages()
// Number of buttons around the current page. // Number of buttons around the current page.
// Excluding first and last page. // Excluding first and last page.
const AroundButtons = 2 const AroundButtons = 2
@ -60,15 +82,15 @@ func (c *Pagination) Compute(ctx vugu.ComputeCtx) {
// Prepare buttons. // Prepare buttons.
buttons := [TotalButtons]paginationButtonInfo{ buttons := [TotalButtons]paginationButtonInfo{
0: {Page: 1}, 0: {Page: 1},
TotalButtons - 1: {Page: c.Pages}, TotalButtons - 1: {Page: pages},
} }
startPage := 2 startPage := 2
if startPage < c.Page-AroundButtons { if startPage < page-AroundButtons {
startPage = c.Page - AroundButtons startPage = page - AroundButtons
} }
if startPage > c.Pages-DynamicButtons { if startPage > pages-DynamicButtons {
startPage = c.Pages - DynamicButtons startPage = pages - DynamicButtons
} }
for i := 0; i < DynamicButtons; i++ { 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. // pageCounter is used to check if pages are strictly monotonically increasing.
buttonsFiltered := make([]paginationButtonInfo, 0, len(buttons)) buttonsFiltered := make([]paginationButtonInfo, 0, len(buttons))
pageCounter := 0 pageCounter := 0
for _, page := range buttons { for _, pageInfo := range buttons {
if pageCounter < page.Page && page.Page <= c.Pages { if pageCounter < pageInfo.Page && pageInfo.Page <= pages {
pageCounter = page.Page pageCounter = pageInfo.Page
if page.Page == c.Page { if pageInfo.Page == page {
page.Class = "d3c-button-highlight" pageInfo.Class = "d3c-button-highlight"
} }
buttonsFiltered = append(buttonsFiltered, page) buttonsFiltered = append(buttonsFiltered, pageInfo)
} }
} }

View File

@ -8,15 +8,14 @@ import (
) )
type PageLayout struct { type PageLayout struct {
Page int `vugu:"data"` Pagination navigation.PaginationInfo `vugu:"data"`
Pages int `vugu:"data"`
} }
func (c *PageLayout) Init(ctx vugu.InitCtx) { 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) { func (c *PageLayout) handlePagination(event navigation.PaginateEvent) {
log.Printf("Some page %v", event.Page) log.Printf("Current pagination info: %v", event.PaginationInfo)
c.Page = event.Page
} }

View File

@ -3,7 +3,7 @@
<h1>Layout</h1> <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> <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> <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> <main:ComponentAddress Name="Max Mustermann" Street="Musterstraße 45" PostalCode="12345" City="Musterstadt" Country="Germany"></main:ComponentAddress>
</layout:Container> </layout:Container>
</div> </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"} vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n\t"}
vgparent.AppendChild(vgn) 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 // ask BuildEnv for prior instance of this specific component
vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*layout.Container) vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*layout.Container)
if vgcomp == nil { 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"} vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n\t\t"}
vgparent.AppendChild(vgn) 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 // ask BuildEnv for prior instance of this specific component
vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*navigation.Pagination) vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*navigation.Pagination)
if vgcomp == nil { if vgcomp == nil {
@ -71,9 +71,8 @@ func (c *PageLayout) Build(vgin *vugu.BuildIn) (vgout *vugu.BuildOut) {
vgin.BuildEnv.WireComponent(vgcomp) vgin.BuildEnv.WireComponent(vgcomp)
} }
vgin.BuildEnv.UseComponent(vgcompKey, vgcomp) // ensure we can use this in the cache next time around vgin.BuildEnv.UseComponent(vgcompKey, vgcomp) // ensure we can use this in the cache next time around
vgcomp.Page = c.Page vgcomp.Bind = &c.Pagination
vgcomp.Pages = c.Pages vgcomp.Paginate = navigation.PaginateFunc(func(event navigation.PaginateEvent) { c.handlePagination(event) })
vgcomp.Pagination = navigation.PaginationFunc(func(event navigation.PaginationEvent) { c.handlePagination(event) })
vgout.Components = append(vgout.Components, vgcomp) vgout.Components = append(vgout.Components, vgcomp)
vgn = &vugu.VGNode{Component: vgcomp} vgn = &vugu.VGNode{Component: vgcomp}
vgparent.AppendChild(vgn) 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"} vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n\t\t"}
vgparent.AppendChild(vgn) 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 // ask BuildEnv for prior instance of this specific component
vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*ComponentAddress) vgcomp, _ := vgin.BuildEnv.CachedComponent(vgcompKey).(*ComponentAddress)
if vgcomp == nil { if vgcomp == nil {