Skip to content

Instantly share code, notes, and snippets.

@Herteby
Last active July 1, 2020 18:22
Show Gist options
  • Save Herteby/ef0a9b4d688a216d9ebdc6968c9a0907 to your computer and use it in GitHub Desktop.
Save Herteby/ef0a9b4d688a216d9ebdc6968c9a0907 to your computer and use it in GitHub Desktop.
module UI.VirtualList exposing (Model, init, view)
import Html exposing (..)
import Html.Attributes exposing (style)
import Html.Events exposing (on)
import Html.Keyed as Keyed
import Json.Decode as Decode
type alias Model =
{ offset : Int, containerHeight : Int }
init : Int -> Model
init height =
{ offset = 0, containerHeight = height }
chunkSize =
3
{-| Display a list of items, only rendering those that are in the viewport
-}
view :
{ itemHeight : Int
, itemView : item -> Html msg
, items : List item
, state : Model
, onScroll : Model -> msg
, node : String
, attrs : List (Attribute msg)
}
-> Html msg
view { itemHeight, itemView, items, state, onScroll, node, attrs } =
let
countToShow =
state.containerHeight // itemHeight + chunkSize
itemsToShow =
(List.drop (state.offset * chunkSize) >> List.take countToShow) items
topMargin =
state.offset * itemHeight * chunkSize
height =
List.length items * itemHeight - topMargin
in
div
([ on "scroll"
(Decode.map2
(\scrollTop clientHeight ->
{ offset = floor scrollTop // itemHeight // chunkSize
, containerHeight = floor clientHeight
}
)
(Decode.at [ "target", "scrollTop" ] Decode.float)
(Decode.at [ "target", "clientHeight" ] Decode.float)
|> Decode.andThen
(\newState ->
if newState == state then
Decode.fail "No difference, ignore"
else
Decode.succeed (onScroll newState)
)
)
, style "overflow-y" "auto"
]
++ attrs
)
[ Keyed.node node
[ style "position" "relative"
, style "top" (String.fromInt topMargin ++ "px")
, style "height" (String.fromInt height ++ "px")
]
(List.indexedMap
(\i item ->
( String.fromInt (i + state.offset), itemView item )
)
itemsToShow
)
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment