module Zipper exposing (Zipper, current, currentPage, fromList, init, isAtEnd, isAtStart, length, next, prev, samePageAs, toEnd, toStart) {-| The Zipper allows to dynamically paginate between items. -} type Zipper a = Zipper { prev : List a , current : a , next : List a } {-| Gets the current item in the zipper. -} current : Zipper a -> a current (Zipper data) = data.current {-| If counting from 1, determines the number corresponding to the currently selected item. -} currentPage : Zipper a -> Int currentPage (Zipper data) = List.length data.prev + 1 {-| Builds a zipper from a list of items. -} fromList : a -> List a -> Zipper a fromList head tail = Zipper { prev = [], current = head, next = tail } {-| Create a new zipper from nothing. -} init : a -> Zipper a init x = Zipper { prev = [], current = x, next = [] } {-| Determines whether the zipper is at the end. -} isAtEnd : Zipper a -> Bool isAtEnd (Zipper data) = List.isEmpty data.next {-| Determines whether the zipper is at the start. -} isAtStart : Zipper a -> Bool isAtStart (Zipper data) = List.isEmpty data.prev {-| Determine the total number of items in the zipper. -} length : Zipper a -> Int length (Zipper data) = List.length data.prev + List.length data.next + 1 {-| Paginates one further in the zipper. -} next : Zipper a -> Zipper a next (Zipper data) = case data.next of [] -> Zipper data head :: tail -> Zipper { prev = data.current :: data.prev , current = head , next = tail } {-| Paginates one back in the zipper. -} prev : Zipper a -> Zipper a prev (Zipper data) = case data.prev of [] -> Zipper data head :: tail -> Zipper { prev = tail , current = head , next = data.current :: data.next } {-| Synchronize a zipper to be at the same page as another, if possible. -} samePageAs : Zipper a -> Zipper a -> Zipper a samePageAs goal z = let cp = currentPage z tp = currentPage goal in if cp == tp then z else if cp < tp then if isAtEnd z then z else samePageAs goal (next z) else if isAtStart z then z else samePageAs goal (prev z) {-| Navigate all the way to the end of the zipper. -} toEnd : Zipper a -> Zipper a toEnd z = if isAtEnd z then z else toEnd (next z) {-| Navigate all the way to the start of the zipper. -} toStart : Zipper a -> Zipper a toStart z = if isAtStart z then z else toStart (prev z)