193 lines
6.6 KiB
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),
|
|
),
|
|
)
|
|
}
|