maragudk / gomponents

HTML components in pure Go.
https://www.gomponents.com
MIT License
1.3k stars 32 forks source link

Table construction helpers? #210

Closed gwd closed 1 month ago

gwd commented 1 month ago

Love gomponents -- made server-side rendering so much better, thanks.

One thing I did that's been quite reusable is the following bit of code to construct row-oriented tables with named headers.

func ThCol(c ...g.Node) g.Node {
    return Th(g.Attr("scope", "col"), g.Group(c))
}

type TableDef[O any] struct {
    header   g.Node
    cellfunc func(O) g.Node
}

func TableHeaders[O any](tdef []TableDef[O]) g.Node {
    return THead(g.Map(tdef, func(t TableDef[O]) g.Node {
        return ThCol(t.header)
    })...)
}

// For each value in O, call all the entryfuncs in tdef
func TableBody[O any](tdef []TableDef[O], vals []O) g.Node {
    return TBody(
        g.Map(vals, func(val O) g.Node {
            return Tr(
                g.Map(tdef, func(t TableDef[O]) g.Node {
                    return Td(t.cellfunc(val))
                })...)

        })...)
}

So suppose you have a slice of things of type langdb.StudyReadunitResponse, and you want to construct a table to display it. You make an array of type TableDef to define how it should be displayed:

    tdef := []TableDef[langdb.StudyReadunitResponse]{
        {g.Text("Cguid"),
            func(r langdb.StudyReadunitResponse) g.Node { return gCguidLink(base, r.Cguid) }},
        {g.Text("Dsum"),
            func(r langdb.StudyReadunitResponse) g.Node { return gFloat(r.Dsum) }},
        {g.Text("Davg"),
            func(r langdb.StudyReadunitResponse) g.Node { return gFloat(r.Davg) }},
        {g.Text("Study Value"),
            func(r langdb.StudyReadunitResponse) g.Node { return gFloat(r.Svsum) }},
        {g.Text("Word count"),
            func(r langdb.StudyReadunitResponse) g.Node { return gPrint(r.Wcount) }},
        {g.Text("View count"),
            func(r langdb.StudyReadunitResponse) g.Node { return gPrint(r.Viewcount) }},
        {g.Text("Score"),
            func(r langdb.StudyReadunitResponse) g.Node { return gFloat(r.Score) }},
    }

And then build your table thus:

        Table(Class("table table-hover"),
            TableHeaders(tdef),
            TableBody(tdef, res)

Is that the kind of thing you'd be interested in adding to the library?

If you think it's too specific, the code is short enough that you could just put it in the documentation somewhere as well, and people could adapt things as they like. At any rate, let me know your thoughts, and thanks again for this project!

markuswustenberg commented 1 month ago

Hi @gwd, thanks for contributing! 😊

I've been thinking about making a repository for gomponents of sorts, perhaps on the official website, for community-contributed components. The reason is that more often than not, I find that components like your table are definitely useful, but need to be tweaked slightly for different use cases.

Therefore, my thinking goes that it's better to be able to find components easily and copy them into one's own code, instead of adding to the core library. Much like in the JS/React ecosystem, where React is the core library, and all sorts of components exist elsewhere.

Thoughts?

gwd commented 1 month ago

I tend to think having a long list of recipes for people to browse / search (i.e., either in a documentation page or a wiki)( is better than having stuff just tucked away in .go files somewhere.

Maybe a "recipes" page / section of a wiki / repository?

But anything (even "let people write their own blogs about how to use gomponents") is fine with me.

markuswustenberg commented 1 month ago

@gwd I created a new repository for this: https://github.com/maragudk/gomponents-gallery

If it turns out to be helpful to people, I might incorporate the repo's components automatically into the website.

Would you like to contribute your table helpers there?