Last active
July 1, 2020 18:22
-
-
Save Herteby/ef0a9b4d688a216d9ebdc6968c9a0907 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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