fable-compiler / Fable.Lit

Write Fable Elmish apps with Lit
https://fable.io/Fable.Lit/
MIT License
91 stars 13 forks source link

Simple SVG is not rendering properly #46

Closed leolorenzoluis closed 2 years ago

leolorenzoluis commented 2 years ago

I am not sure if this is Lit related or just Fable, but given the following code:

let createBar index value =
    let ratio = 240 / 10
    html $"""
            <rect 
              fill="red"
              x={( 320 / 10 )  * index}
              y={240 - ( value * ratio )}
              width={320 / 10}
              height="{value * ratio}"/>
        """

let chart () =
    html $"""
    <svg width="320" height="240">
      {
       seq { 1..11 } |> Seq.mapi(createBar)
      }
    </svg>
    """

It seems that the <rect> is not being rendered properly. I can't seem to find any difference between the text, but when I try to edit the HTML and just remove either the space in between <rect> or something then the rect renders.

<svg width="320" height="240">
    <!--?lit$646399511$--><!---->
    <rect fill="red" x="0" y="216" width="32" height="24">
    </rect><!----><!---->
    <rect fill="red" x="32" y="192" width="32" height="48">
    </rect><!----><!---->
    <rect fill="red" x="64" y="168" width="32" height="72">
    </rect><!----><!---->
    <rect fill="red" x="96" y="144" width="32" height="96">
    </rect><!----><!---->
    <rect fill="red" x="128" y="120" width="32" height="120">
    </rect><!----><!---->
    <rect fill="red" x="160" y="96" width="32" height="144">
    </rect><!----><!---->
    <rect fill="red" x="192" y="72" width="32" height="168">
    </rect><!----><!---->
    <rect fill="red" x="224" y="48" width="32" height="192">
    </rect><!----><!---->
    <rect fill="red" x="256" y="24" width="32" height="216">
    </rect><!----><!---->
    <rect fill="red" x="288" y="0" width="32" height="240">
    </rect><!----><!---->
    <rect fill="red" x="320" y="-24" width="32" height="264">
    </rect><!---->
    </svg>

It's being called from a HookComponent

[<HookComponent>]
let Page() =
       html $"""{chart()}"""

I tried changing Seq.mapi to Lit.mapUnique but that didn't make a difference.

Also when I try to set a breakpoint on chart function then the function is called twice? Is that expected? Is it due to some lifecycle?

alfonsogarciacaro commented 2 years ago

Don't remember exactly why but Lit requires you to use svg instead of html to render templates within SVG: https://lit.dev/docs/api/templates/#svg

With Fable.Lit you just need to replace html with svg. The tricky part is that when rendering the outer <svg></svg> element is OK to use html but not with a nested template. So in your example above you need to change the first html in createBar but not the one in chart(). There's a sample in this repo: https://github.com/fable-compiler/Fable.Lit/blob/6317d3a32ed91927984b1fc03aa7da3fc02affa3/sample/Clock.fs#L99-L114

Note that when creating the SVG element html is used: https://github.com/fable-compiler/Fable.Lit/blob/6317d3a32ed91927984b1fc03aa7da3fc02affa3/sample/Clock.fs#L177-L194

Sorry this is a bit confusing, it's like this in the Lit library and I didn't find a way to circumvent it.

leolorenzoluis commented 2 years ago

No worries, still trying to grok web components too. Is this related? https://twitter.com/rich_harris/status/1141831377828102145?lang=en

For future readers here's the solution:

let createBar index value =
    let ratio = 240 / 10
    svg $"""
            <rect 
              fill="red"
              x={( 320 / 10 )  * index}
              y={240 - ( value * ratio )}
              width={320 / 10}
              height="{value * ratio}"/>
        """

let chart () =
    html $"""
    <svg width="320" height="240">
      {
       seq { 1..11 } |> Seq.mapi(createBar)
      }
    </svg>
    """
alfonsogarciacaro commented 2 years ago

Oh, maybe... I thought Lit templates were independent of web components, but apparently in this case they're related. This is what the Lit documentation says:

image