elm-ui icon indicating copy to clipboard operation
elm-ui copied to clipboard

Unexpected scrolling/layout behavior

Open gampleman opened this issue 7 years ago • 14 comments
trafficstars

https://ellie-app.com/3TtR8yh7Rw5a1

Expected behavior

column [ width fill, height fill, spacing 30 ]
        [ myElement
        , column [ width fill, height fill ] [
            column [ width (px 350), height fill, Element.scrollbarY, Border.width 1 ] -- here
                [ column [ width fill ] <|
                    el [] (text "this should scroll indepedendently")
                        :: List.map (always myElement) (List.range 0 100)
                ]
            , el [] (text "this should be at the bottom of the screen")
            ]
        ]

The column above is marked height fill, so one would expect it to fill the container. It has children that overflow that height, but Element.scrollbarY has been set there.

So my expectation would be that the marked column would take the remaining space of the container but would allow all its children to scroll.

Versions

  • OS: OSX
  • Browser: chrome
  • Browser Version: 70
  • Elm Version: 0.19
  • Elm UI Version: 1.0.0

Motivation

This came up when building a sidebar for our application where in the main area the user can make a selection and related data to the selected items appears in the sidebar. We wanted the navigation and other items to remain visible in the sidebar, but have just the related data part scroll.

gampleman avatar Nov 15 '18 15:11 gampleman

Yup. I ran into this exact behavior when I was learning Elm by building a browser for my photos. I wanted a top bar that allowed me to change the viewing mode and only the view would scroll.

        [ (Element.layout [] <|
            column [ width fill, height fill ]
            [ row
                [ paddingXY 20 0
                , spacing 10
                , verticalGrayGradient
                , width fill
                , height (px 60)
                ]

                [ modeButton "Grid" Grid
                , modeButton "Timeline" Timeline
                , modeButton "Slideshow" Slideshow
                , Input.button
                    [ alignRight ]
                    { onPress = Just TodoInput
                    , label = Element.el
                        [ padding 8
                        , verticalBlueGradient
                        , Border.rounded 3
                        , Font.color offWhite
                        ]
                        (text "Autoplay")
                    }
                ]
            , el
                [width fill, height fill, scrollbarY] -- !!! PROBLEM RIGHT HERE !!!
                (case model.appMode of
                    Grid ->
                        (viewGrid model)
                    Timeline ->
                        (viewTimeline model)
                    Slideshow ->
                        none  -- TODO. Not implemented
                )
            ]
        )]

The viewGrid and viewTimeline functions actually handle the rendering of the view but the idea is that the containing el will be constrained to the screen size so whatever DOM elements those functions produce will scroll inside of the el rather than causing the el to keep growing. Correct me if I am wrong about this expectation as I still an Elm n3wb.

Anyways, I worked around this by keeping the Viewport information from Browser.Dom around in my model and forcing the maximum size of my container el to be whatever was left after subtracting out the top bar. Note that this only works however if you know the size of all of your elements.

-- Problematic line changes to this
[width fill, height (fill |> maximum (Basics.round (model.viewport.viewport.height - 60.0))), scrollbarY]

Because AFAIK, we don't know IDs of anything elm-ui produces so Browser.Dom.getViewportFor is of no use.

ghost avatar Jan 18 '19 18:01 ghost

I discovered by accident that a workaround is to add scrollbarY to the parent of the intended container, see https://ellie-app.com/538HjqscB6ka1

jhbrown94 avatar Mar 20 '19 17:03 jhbrown94

I've done some digging. I think the root cause is that flex-shrink is set to 0 except in scrolling elements. So if a would-be scrolling element is contained in something which doesn't have a fixed size, that container will grow indefinitely rather than triggering the scrollbars. Liberal application of htmlAttribute <| HtmlAttr.style "flex-shrink" "1" to containers resolves the issue for me.

@mdgriffith Would something break if you just set flex-shrink to 1 for everything?

jhbrown94 avatar Mar 21 '19 19:03 jhbrown94

Wait, is this just #12?

jhbrown94 avatar Mar 21 '19 19:03 jhbrown94

The more I read, the more complicated this sounds. flex-shrink might not be sufficient? I'm unclear. https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size

jhbrown94 avatar Mar 21 '19 19:03 jhbrown94

In the past I've found wrapping children in a div with position absolute can help resolve flexbox issues. If you're stuck you can use this helper for now

scrollbarYEl : List (Attribute msg) -> Element msg -> Element msg
scrollbarYEl attrs body =
    el [ height fill, width fill ] <|
        el
            ([ htmlAttribute <| Html.Attributes.style "position" "absolute"
             , htmlAttribute <| Html.Attributes.style "top" "0"
             , htmlAttribute <| Html.Attributes.style "right" "0"
             , htmlAttribute <| Html.Attributes.style "bottom" "0"
             , htmlAttribute <| Html.Attributes.style "left" "0"
             , Element.scrollbarY
             ]
                ++ attrs
            )
            body

opsb avatar Mar 24 '19 09:03 opsb

Anyone figure a good solution to this?

rex891 avatar Sep 12 '19 23:09 rex891

Indeed, experiencing this issue as well.

cmditch avatar Sep 30 '19 20:09 cmditch

@kmBlaine your post got me thinking - I was able to do some measuring by putting in an HTML element of zero size that has an ID, and then doing a getViewportFor on it. You have to add in the padding afterwards.

topBar : Model a -> Element Msg
topBar model =
    E.row
        [ E.width E.fill]
        [ EI.button buttonStyle { label = E.text "select pdf", onPress = Just SelectClick }
        <... more stuff ... >
        , E.html <|  Html.div [ HA.id "topbarheight", HA.style "height" "100%" ] [ Html.text "" ]
        ]
    , Task.attempt TopViewport <| Browser.Dom.getViewportOf "topbarheight"

bburdette avatar Nov 19 '19 00:11 bburdette

the way I worked around it was by adding htmlAttribute <| Html.Attributes.style "height" "100vh" to the outer row and adding scrollbarY to the columns, and it worked.

Mox93 avatar Feb 25 '20 13:02 Mox93

Also happens with elm-ui 1.1.5. #bug #has-ellie

alexkorban avatar Apr 30 '20 03:04 alexkorban

I want this fixed. It's the only thing that has ever frustrated me a lot when using Elm.

dullbananas avatar Jul 15 '20 21:07 dullbananas

Maybe having an easy workaround listed somewhere would help, since this is a hard-to-fix issue. There's a few listed in this thread, but maybe there could be one canonical solution.

tankorsmash avatar Nov 18 '21 02:11 tankorsmash

@opsb 's solution was the most straightforward to apply for me.

Maldus512 avatar Mar 25 '22 14:03 Maldus512