Files
kjol/go/webui/prettytable.go

193 lines
6.6 KiB
Go

package webui
import "kjol/vdom"
// Port of web/kit/PrettyTable.tsx.
//
// PrettyTable renders only the table chrome: the container/wrapper, the styled
// <thead>, and an empty-styled <tbody> into which the CALLER passes body rows as
// children. That matches the TSX, whose body is `props.children` (rows built with
// AutoTable's TdLeft/TdRight/TdCenter cells). Those cell helpers live in AutoTable
// (not ported here), so callers build rows with the vdom tag builders directly,
// e.g. vdom.Tr(vdom.Td(vdom.Attr("class","text-right"), vdom.Text(...))).
// (CellGrid is the component that models columns + a per-cell Render func.)
//
// The enum values, class maps, and shared class strings that PrettyTable.tsx
// imports from AutoTable.tsx are inlined here (prefixed pt*/PrettyTable*) so this
// file compiles on its own and never clashes with a parallel AutoTable port.
// PrettyTableColumnPosition controls header + cell alignment.
type PrettyTableColumnPosition int
const (
PrettyTableColLeft PrettyTableColumnPosition = 0
PrettyTableColRight PrettyTableColumnPosition = 1
PrettyTableColCenter PrettyTableColumnPosition = 2
)
// PrettyTableHeaderColor selects the header background/text palette.
type PrettyTableHeaderColor int
const (
PrettyTableColorDefault PrettyTableHeaderColor = 0
PrettyTableColorBlue PrettyTableHeaderColor = 1
PrettyTableColorGreen PrettyTableHeaderColor = 2
PrettyTableColorGray PrettyTableHeaderColor = 3
PrettyTableColorDarkBlue PrettyTableHeaderColor = 4
)
// PrettyTableSize selects header/body padding density.
type PrettyTableSize int
const (
PrettyTableSizeDefault PrettyTableSize = 0
PrettyTableSizeCompact PrettyTableSize = 1
PrettyTableSizeSuperCompact PrettyTableSize = 2
)
const ptTblContainer = "relative flex flex-col w-full h-full bg-surface rounded-default overflow-hidden"
const ptTblWrapper = "overflow-x-auto w-full"
const ptTblBase = "min-w-full"
const ptHeaderContent = "transition-transform duration-150 ease-in-out"
const ptHeaderInnerBase = "flex justify-between gap-2 items-center"
var ptHeaderColorCls = map[PrettyTableHeaderColor]string{
PrettyTableColorDefault: "bg-surface-muted",
PrettyTableColorBlue: "bg-sky-700 text-white",
PrettyTableColorGreen: "bg-green-700 text-white",
PrettyTableColorGray: "bg-neutral-600 text-white",
PrettyTableColorDarkBlue: "bg-sky-900 text-white",
}
var ptHeaderTextCls = map[PrettyTableHeaderColor]string{
PrettyTableColorDefault: "font-bold uppercase tracking-wider",
PrettyTableColorBlue: "font-semibold",
PrettyTableColorGreen: "font-semibold",
PrettyTableColorGray: "font-semibold",
PrettyTableColorDarkBlue: "font-semibold",
}
// ptRowHoverCls is applied to the tbody when Hover is on (matches the TSX
// PT_ROW_HOVER_CLS map).
var ptRowHoverCls = map[PrettyTableHeaderColor]string{
PrettyTableColorDefault: "[&_tr:hover]:!bg-surface-strong",
PrettyTableColorBlue: "[&_tr:hover]:!bg-sky-100 dark:bg-sky-900",
PrettyTableColorGreen: "[&_tr:hover]:!bg-green-100 dark:bg-green-900",
PrettyTableColorGray: "[&_tr:hover]:!bg-surface-strong",
PrettyTableColorDarkBlue: "[&_tr:hover]:!bg-sky-100 dark:bg-sky-900",
}
var ptHeaderPaddingCls = map[PrettyTableSize]string{
PrettyTableSizeDefault: "p-4 text-sm",
PrettyTableSizeCompact: "py-1.5 px-2 text-sm",
PrettyTableSizeSuperCompact: "py-1 px-2 text-ss",
}
var ptBodyPaddingCls = map[PrettyTableSize]string{
PrettyTableSizeDefault: "text-sm [&_td]:p-4",
PrettyTableSizeCompact: "text-sm [&_td]:py-1 [&_td]:px-2",
PrettyTableSizeSuperCompact: "text-ss [&_td]:py-0.5 [&_td]:px-2",
}
var ptPosCls = map[PrettyTableColumnPosition]string{
PrettyTableColLeft: "text-left",
PrettyTableColRight: "text-right",
PrettyTableColCenter: "text-center",
}
var ptHeaderInnerPos = map[PrettyTableColumnPosition]string{
PrettyTableColLeft: "",
PrettyTableColRight: "flex-row-reverse",
PrettyTableColCenter: "justify-center",
}
// PrettyTableColumn is one header column definition.
type PrettyTableColumn struct {
DisplayName string
DisplayPosition PrettyTableColumnPosition
HeaderClasses string
}
// PrettyTableOptions configures the table chrome. The Go zero value matches the
// TSX defaults exactly (size default, all flags off, default color, left align),
// so an unset PrettyTableOptions{} needs no merge.
type PrettyTableOptions struct {
Size PrettyTableSize
Shadow bool
Hover bool
Alternate bool
HeaderBorderY bool
SurroundingBorder bool
BorderX bool
BorderY bool
Color PrettyTableHeaderColor
TableLayoutAuto bool
}
func prettyTableBodyClass(o PrettyTableOptions) string {
c := ptBodyPaddingCls[o.Size]
if o.BorderY {
c = cx(c, "[&_td+td]:border-l [&_td+td]:border-line-strong")
}
if o.Alternate {
c = cx(c, "[&_tr:nth-child(even)]:bg-surface-raised")
}
if o.BorderX {
c = cx(c, "[&_tr:not(:last-child)]:border-b [&_tr:not(:last-child)]:border-line-strong")
}
if o.Hover {
c = cx(c, ptRowHoverCls[o.Color])
}
return c
}
// PrettyTable renders the table container + styled header, with body rows passed
// as children (see the file comment). opts may be the zero value for defaults.
func PrettyTable(columns []PrettyTableColumn, opts PrettyTableOptions, children ...*vdom.VNode) *vdom.VNode {
containerCls := ptTblContainer
if opts.SurroundingBorder {
containerCls = cx(containerCls, "border border-line-strong")
}
if opts.Shadow {
containerCls = cx(containerCls, "shadow-sm")
}
tableCls := ptTblBase
if !opts.TableLayoutAuto {
tableCls = cx(tableCls, "table-fixed")
}
var headerCells []*vdom.VNode
for displayIdx, col := range columns {
pos := col.DisplayPosition
thCls := cx(ptHeaderPaddingCls[opts.Size], ptHeaderColorCls[opts.Color], ptPosCls[pos])
if opts.HeaderBorderY && displayIdx > 0 {
thCls = cx(thCls, "border-l border-l-neutral-300")
}
thCls = cx(thCls, col.HeaderClasses)
innerCls := cx(ptHeaderInnerBase, ptHeaderInnerPos[pos])
headerCells = append(headerCells, vdom.Th(vdom.Attr("class", thCls),
vdom.Div(vdom.Attr("class", ptHeaderContent),
vdom.Div(vdom.Attr("class", innerCls),
vdom.Div(vdom.Attr("class", cx("grow text-sm", ptHeaderTextCls[opts.Color])),
vdom.Text(col.DisplayName),
),
),
),
))
}
thead := vdom.Thead(vdom.Attr("class", "[&_th]:border-b [&_th]:border-line-strong"),
vdom.Tr(kids(nil, headerCells)...),
)
tbody := vdom.Tbody(kids([]vdom.Mod{vdom.Attr("class", prettyTableBodyClass(opts))}, children)...)
return vdom.Div(vdom.Attr("class", containerCls),
vdom.Div(vdom.Attr("class", ptTblWrapper),
vdom.Table(vdom.Attr("class", tableCls), thead, tbody),
),
)
}