initial port of the UI kit
This commit is contained in:
194
go/webui/prettytable.go
Normal file
194
go/webui/prettytable.go
Normal file
@@ -0,0 +1,194 @@
|
||||
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.El("tr", vdom.El("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-white 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-neutral-50",
|
||||
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-neutral-200",
|
||||
PrettyTableColorBlue: "[&_tr:hover]:!bg-sky-100",
|
||||
PrettyTableColorGreen: "[&_tr:hover]:!bg-green-100",
|
||||
PrettyTableColorGray: "[&_tr:hover]:!bg-neutral-200",
|
||||
PrettyTableColorDarkBlue: "[&_tr:hover]:!bg-sky-100",
|
||||
}
|
||||
|
||||
var ptHeaderPaddingCls = map[PrettyTableSize]string{
|
||||
PrettyTableSizeDefault: "p-4 text-sm",
|
||||
PrettyTableSizeCompact: "py-1.5 px-2 text-sm",
|
||||
PrettyTableSizeSuperCompact: "py-1 px-2 text-xs",
|
||||
}
|
||||
|
||||
var ptBodyPaddingCls = map[PrettyTableSize]string{
|
||||
PrettyTableSizeDefault: "text-sm [&_td]:p-4",
|
||||
PrettyTableSizeCompact: "text-sm [&_td]:py-1 [&_td]:px-2",
|
||||
PrettyTableSizeSuperCompact: "text-xs [&_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-neutral-300")
|
||||
}
|
||||
if o.Alternate {
|
||||
c = cx(c, "[&_tr:nth-child(even)]:bg-neutral-100")
|
||||
}
|
||||
if o.BorderX {
|
||||
c = cx(c, "[&_tr:not(:last-child)]:border-b [&_tr:not(:last-child)]:border-neutral-300")
|
||||
}
|
||||
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-neutral-300")
|
||||
}
|
||||
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.El("th",
|
||||
vdom.Attr("class", thCls),
|
||||
vdom.El("div", vdom.Attr("class", ptHeaderContent),
|
||||
vdom.El("div", vdom.Attr("class", innerCls),
|
||||
vdom.El("div", vdom.Attr("class", cx("grow text-sm", ptHeaderTextCls[opts.Color])),
|
||||
vdom.Text(col.DisplayName),
|
||||
),
|
||||
),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
thead := vdom.El("thead",
|
||||
vdom.Attr("class", "[&_th]:border-b [&_th]:border-neutral-300"),
|
||||
vdom.El("tr", kids(nil, headerCells)...),
|
||||
)
|
||||
|
||||
tbody := vdom.El("tbody", kids([]vdom.Mod{vdom.Attr("class", prettyTableBodyClass(opts))}, children)...)
|
||||
|
||||
return vdom.El("div", vdom.Attr("class", containerCls),
|
||||
vdom.El("div", vdom.Attr("class", ptTblWrapper),
|
||||
vdom.El("table", vdom.Attr("class", tableCls), thead, tbody),
|
||||
),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user