mdgriffith / elm-ui

What if you never had to write CSS again?
https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/
BSD 3-Clause "New" or "Revised" License
1.36k stars 111 forks source link

Add a function similar to ZStack in SwiftUI #342

Open dullbananas opened 2 years ago

dullbananas commented 2 years ago

It would be like row or column except it puts each element on top of the previous one

NduatiK commented 2 years ago

Assuming you want a solution and don't care if its the library that does it.

You can pull this off using the inFront attribute. Ellie

zStack attrs children =
    case children of
        [] ->
            none
        bottomMost :: otherChildren ->
            el 
                (inFront (zStack attr otherChildren) :: attr)        
                bottomMost 

then

module Main exposing (..)

import Element exposing (Element, alignRight, centerY, el, fill, fillPortion, height, padding, px, rgb, rgb255, rgba, row, spacing, text, width)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Element.Input as Input

type Msg
    = Noop Float

main =
    Element.layout [] content

content =
    zStack [width fill]
    [ text "Bottom"
    , el [Element.moveDown 10] ( text "Middle" )
    , el [Element.moveDown 20] (text "Top")
    ] 

zStack attrs children =
    case children of
        [] ->
            Element.none
        bottomMost :: otherChildren ->
            el 
                (Element.inFront (zStack attrs otherChildren) :: attrs)        
                bottomMost 
coinop-logan commented 3 months ago

@NduatiK's suggested approach is pretty good. But I want to add a bit of weight to the desire for more formal application of this principle. I should say I'm arguing solely from the perspective of ergonomics, and have no idea how complex this would be to actually implement...

For me, the simplicity and elegance of rows and columns, combined with the way Element.Lengths work, is where a lot of where elm-ui's pleasure comes from. It would be so nice if you could do this in the z direction. If this was implemented, each element would essentially be 3D by default, but this would be hidden until you started making zStacks. Using inFront successively lets you stack elements nicely, but you can't use Lengths in the z direction.

Now, is this really necessary to strive for? I can't say I've ever needed an Element to "fill" the whole z direction (or what that would mean exactly). And a "pixel" length doesn't make much sense - the analog would be a thin layer, I guess? So I can't say this looks like it would be simple to do.

BUT. If I could just as thoughtlessly extend elements in the z direction as I do in X and Y with rows and columns, that'd be so nice. As it is, every time I need to layer stuff in elm-ui, I stumble a bit and have to go to some old project, or search around a bit, to figure out how exactly to set it up.

I also wonder if formalizing this behavior in this way might clear up some of the other issues around inFront, in a sort of side effect..? Especially those around intuitions of borders, like #201?

Just my 2 cents!

coinop-logan commented 3 months ago

Another issue with the inFront workaround btw, is that every element inFront of some other Element is "outside" the scheme of sizes that makes elm-ui such a lovely thing to work with. For example, if a small Element has a much larger Element inFront of it, then the page layout will still treat the combined element as small, leading to things like (in my case) an Element that is long enough to give the page a scrollbar, without the main page Element extending that far, thus leading to the user scrolling down and seeing my background end before the end of the page.