90 lines
3.3 KiB
Go
90 lines
3.3 KiB
Go
package webui
|
|
|
|
import "kjol/vdom"
|
|
|
|
// Port of web/kit/General.tsx. Anchor components render plain <a href>; the app
|
|
// wires SPA navigation on top (e.g. its layout nav uses Button/OnClick).
|
|
|
|
// PageContainer wraps a page body (marker class kept for app CSS).
|
|
func PageContainer(children ...*vdom.VNode) *vdom.VNode {
|
|
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "admin-page-container")}, children)...)
|
|
}
|
|
|
|
// Divider is a thin horizontal rule.
|
|
func Divider() *vdom.VNode { return vdom.Hr(vdom.Attr("class", "text-neutral-200 mt-1 mb-3")) }
|
|
|
|
// CodeBox renders a dark monospace code block.
|
|
func CodeBox(code, class string) *vdom.VNode {
|
|
return vdom.Div(vdom.Attr("class", cx("text-xs p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default", class)),
|
|
vdom.Pre(vdom.Code(vdom.Text(code))),
|
|
)
|
|
}
|
|
|
|
// PageHeader is a centered page title with an underline.
|
|
func PageHeader(text, class string) *vdom.VNode {
|
|
return vdom.Header(vdom.Attr("class", class),
|
|
vdom.Div(vdom.Attr("class", "mt-1"),
|
|
vdom.H1(vdom.Attr("class", "text-center text-2xl font-light text-neutral-800 mb-2"), vdom.Text(text)),
|
|
vdom.Hr(vdom.Attr("class", "text-neutral-200 mb-2")),
|
|
),
|
|
)
|
|
}
|
|
|
|
// PageLink is an inline text link (blue). newTab opens in a new tab.
|
|
func PageLink(href string, newTab bool, class string, children ...*vdom.VNode) *vdom.VNode {
|
|
mods := []vdom.Mod{
|
|
vdom.Attr("href", href),
|
|
vdom.Attr("class", cx("text-sky-700 hover:text-sky-800 hover:underline hover:decoration-1", class)),
|
|
}
|
|
if newTab {
|
|
mods = append(mods, vdom.Attr("target", "_blank"), vdom.Attr("rel", "noopener noreferrer"))
|
|
}
|
|
return vdom.A(kids(mods, children)...)
|
|
}
|
|
|
|
// Loader is a centered spinning ring.
|
|
func Loader() *vdom.VNode {
|
|
return vdom.Div(vdom.Attr("class", "flex items-center justify-center p-8"),
|
|
vdom.Div(vdom.Attr("class", "h-8 w-8 border-4 border-sky-700 border-t-transparent rounded-full animate-spin")),
|
|
)
|
|
}
|
|
|
|
// BreadcrumbItem is one crumb (URL + display text).
|
|
type BreadcrumbItem struct {
|
|
URL string
|
|
DisplayText string
|
|
}
|
|
|
|
// Breadcrumbs renders a chevron-separated crumb trail; the last item is bold and
|
|
// not linked.
|
|
func Breadcrumbs(items []BreadcrumbItem) *vdom.VNode {
|
|
mods := []vdom.Mod{vdom.Attr("class", "flex flex-row items-center text-neutral-400 text-xs")}
|
|
for i, crumb := range items {
|
|
if i != len(items)-1 {
|
|
mods = append(mods, vdom.Span(vdom.Attr("class", "flex items-center"),
|
|
vdom.A(vdom.Attr("href", crumb.URL),
|
|
vdom.Attr("class", "text-neutral-500 cursor-pointer no-underline hover:text-neutral-700 hover:underline"),
|
|
vdom.Text(crumb.DisplayText)),
|
|
Icon("chevron-right", 12, "mx-[0.15rem] opacity-50"),
|
|
))
|
|
} else {
|
|
mods = append(mods, vdom.Span(vdom.Attr("class", "text-neutral-700 font-medium"), vdom.Text(crumb.DisplayText)))
|
|
}
|
|
}
|
|
return vdom.Div(mods...)
|
|
}
|
|
|
|
// ManagerPageHeader is a title/description row with an optional action slot
|
|
// (marker classes kept for app CSS).
|
|
func ManagerPageHeader(title, description string, action *vdom.VNode) *vdom.VNode {
|
|
inner := vdom.Div(vdom.H2(vdom.Attr("class", "page-title"), vdom.Text(title)))
|
|
if description != "" {
|
|
inner.Children = append(inner.Children, vdom.P(vdom.Attr("class", "page-desc"), vdom.Text(description)))
|
|
}
|
|
mods := []vdom.Mod{vdom.Attr("class", "page-header"), inner}
|
|
if action != nil {
|
|
mods = append(mods, action)
|
|
}
|
|
return vdom.Div(mods...)
|
|
}
|