rstudio / htmltools

Tools for HTML generation and output
https://rstudio.github.io/htmltools/
215 stars 68 forks source link

error: "cannot mtfrm" - what is it? #406

Closed stla closed 1 year ago

stla commented 1 year ago

Hello,

I'm trying to convert the following HTML code to R code by using tag:

    <flex class="v" style="height: 500px">
        <flex-item style="flex: 1; background: red">Flex 1</flex-item>
        <flex-resizer></flex-resizer>
        <flex-item style="flex: 1; background: blue">
            <flex class="h">
                <flex-item style="flex: 1">Flex 2</flex-item>
                <flex-resizer></flex-resizer>
                <flex-item style="flex: 2; background: green">
                    <flex class="v">
                        <flex-item style="flex: 1; background: pink;">Flex 3</flex-item>
                        <flex-resizer></flex-resizer>
                        <flex-item style="flex: 1">
                            <flex class="h">
                                <flex-item style="flex: 1">Flex 4</flex-item>
                                <flex-resizer></flex-resizer>
                                <flex-item style="flex: 2; background: yellow">Flex 5</flex-item>
                                <flex-item style="flex: 2; background: yellow">Flex 6</flex-item>
                            </flex>
                        </flex-item>
                    </flex>
                </flex-item>
            </flex>
        </flex-item>
    </flex>

This is hard by the way. Here is my R code:

  tag(
    "flex", 
    list(
      class = "v", style = "height: 500px;",
      tag(
        "flex-item", 
        list(
          style = "flex: 1; background: red;", 
          "Flex 1"
        )
      ),
      tag(
        "flex-resizer",
        list()
      ),
      tag(
        "flex-item", 
        list(
          style = "flex: 1; background: blue;", 
          tag(
            "flex",
            list(
              class = "h",
              tag(
                "flex-item", 
                list(
                  style = "flex: 1;", 
                  "Flex 2"
                )
              )
            ),
            tag(
              "flex-resizer",
              list()
            ),
            tag(
              "flex-item", 
              list(
                style = "flex: 2; background: green;", 
                tag(
                  "flex",
                  list(
                    class = "v",
                    tag(
                      "flex-item", 
                      list(
                        style = "flex: 1; background: pink;", 
                        "Flex 3"
                      )
                    ),
                    tag(
                      "flex-resizer",
                      list()
                    ),
                    tag(
                      "flex-item", 
                      list(
                        style = "flex: 1;", 
                        tag(
                          "flex",
                          list(
                            class = "h",
                            tag(
                              "flex-item",
                              list(
                                style = "flex: 1;",
                                "Flex 4"
                              )
                            ) 
                          ),
                          tag(
                            "flex-resizer",
                            list()
                          ),
                          tag(
                            "flex-item",
                            list(
                              style = "flex: 2; background: yellow;",
                              "Flex 5"
                            )
                          ),
                          tag(
                            "flex-item",
                            list(
                              style = "flex: 2; background: yellow;",
                              "Flex 6"
                            )
                          )
                        )
                      )
                    )                    
                  )
                )
              )
            )
          )
        )
      )
    )  
  )

When I run this code, I get:

Error in mtfrm.default(list(name = "flex-resizer", attribs = list(), children = list())) : 
  cannot mtfrm

What does that mean? I don't find the function mtfrm. Any hint?

gadenbuie commented 1 year ago

Yeah, that looks pretty painful! I think the items starting on line 32 of your reprex are actually meant to be contained in the list of (line 22) of the parent container.

The error you're getting is essentially this:

tag("flex-item", list(), tag("flex-resizer", list()))
#> Error in mtfrm.default(list(name = "flex-resizer", attribs = list(), children = list())) : 
#>   cannot mtfrm

Where you probably intended

tag("flex-item", list(tag("flex-resizer", list())))
#> <flex-item>
#>   <flex-resizer></flex-resizer>
#> </flex-item>

I'd highly recommend wrapping up these tags into helper functions:

flex <- function(...) tags("flex", list(...))
flex_resizer <- function() tags("flex-resizer", list())
flex_item <- function(..., flex = 2) {
  tags("flex-item", list(..., style = css(flex = flex))
}

flex(
  class = "v",
  style = css("height" = "500px"),
  flex_item(flex = 1, "Flex 1"),
  flex_resizer()
  #...
)
gadenbuie commented 1 year ago

btw I hadn't heard of mtfrm, but it's apparently a base function called by %in%. It's throwing because there's a tag object being passed to .noWS by accident.

stla commented 1 year ago

Thanks! I've found the mistakes. It works.

Look what is it: https://jsfiddle.net/6j10L3x2/1/. The boxes are resizable. Will try to put shiny widgets in them.

It would be nice if we could add some new CSS elements to tags, such as tags$flex. If this were possible I could get the R code within a second with my package HTML2R.

stla commented 1 year ago

This doesn't work as I expected. Because when I increase the height of the small plot, then it covers the big plot, the height of the big plot does not decrease as I expected. I tried height="auto" and height="100%".

ggflex

stla commented 1 year ago

Hmm... that works as desired when I resize the window. Maybe one has to trigger a resize event.

stla commented 1 year ago

Yes! This works by triggering the resize event when the mousedown event is triggered:

ggflex

Do you know whether there exists something similar in a Shiny package? That looks interesting IMHO, and maybe that deserves to be further developed.

gadenbuie commented 1 year ago

That's pretty neat! And you're right, emitting a resize event on window is the best way to tell shiny that some of the plots on the page have updated.

It would be nice if we could add some new CSS elements to tags, such as tags$flex.

I definitely agree that tag() isn't a very ergonomic function to call directly, which is why I recommend writing a wrapper function. For now, the tags available in tags$ are limited to the official HTML elements and tag() is available for custom elements. Relatedly, <flex> and <flex-item> and the other elements in your example aren't official HTML elements, they're custom tags that are used by the JavaScript library you're using.

stla commented 1 year ago

There's no JavaScript library. These tags are simply defined in the CSS file (I didn't know this is possible - is it equivalent to a div with another name?).

That's pretty neat!

Thanks. Maybe I will continue to work on this stuff, at least to write a blog post if I achieve something valuable.

gadenbuie commented 1 year ago

There's no JavaScript library. These tags are simply defined in the CSS file

Oh yeah, the HTML spec and parsers are really forgiving! One thing to watch out for is that custom elements should have a - in the name to distinguish them from regular elements. (That's clearly not strictly enforced in the parser or the CSS, but it's good practice.)