Add landing page for kjol, documentation

This commit is contained in:
2026-07-13 16:51:21 -04:00
parent 5230bd6702
commit fec8ef4a3e
54 changed files with 3529 additions and 547 deletions

View File

@@ -80,7 +80,7 @@ const (
// HEADER_COLOR_CLS is the background + text color per header color.
var HEADER_COLOR_CLS = map[AutoTableHeaderColor]string{
AUTOTABLE_HEADER_COLOR_DEFAULT: "bg-neutral-50",
AUTOTABLE_HEADER_COLOR_DEFAULT: "bg-surface-muted",
AUTOTABLE_HEADER_COLOR_BLUE: "bg-sky-700 text-white",
AUTOTABLE_HEADER_COLOR_GREEN: "bg-green-700 text-white",
AUTOTABLE_HEADER_COLOR_GRAY: "bg-neutral-600 text-white",
@@ -89,7 +89,7 @@ var HEADER_COLOR_CLS = map[AutoTableHeaderColor]string{
// atHeaderSortHoverCls is the sortable-hover override per color.
var atHeaderSortHoverCls = map[AutoTableHeaderColor]string{
AUTOTABLE_HEADER_COLOR_DEFAULT: "hover:bg-neutral-300",
AUTOTABLE_HEADER_COLOR_DEFAULT: "hover:bg-surface-strong",
AUTOTABLE_HEADER_COLOR_BLUE: "hover:bg-sky-500",
AUTOTABLE_HEADER_COLOR_GREEN: "hover:bg-green-800",
AUTOTABLE_HEADER_COLOR_GRAY: "hover:bg-neutral-500",
@@ -107,7 +107,7 @@ var HEADER_TEXT_CLS = map[AutoTableHeaderColor]string{
// atHeaderSortIconCls is the sort-icon color (matches header text) per color.
var atHeaderSortIconCls = map[AutoTableHeaderColor]string{
AUTOTABLE_HEADER_COLOR_DEFAULT: "text-black",
AUTOTABLE_HEADER_COLOR_DEFAULT: "text-ink",
AUTOTABLE_HEADER_COLOR_BLUE: "text-white",
AUTOTABLE_HEADER_COLOR_GREEN: "text-white",
AUTOTABLE_HEADER_COLOR_GRAY: "text-white",
@@ -137,11 +137,11 @@ var atPaginationPaddingCls = map[AutoTableSize]string{
// atRowHoverCls is the body-row hover background per color (when hover is on).
var atRowHoverCls = map[AutoTableHeaderColor]string{
AUTOTABLE_HEADER_COLOR_DEFAULT: "hover:bg-neutral-200",
AUTOTABLE_HEADER_COLOR_BLUE: "hover:bg-sky-100",
AUTOTABLE_HEADER_COLOR_GREEN: "hover:bg-green-100",
AUTOTABLE_HEADER_COLOR_GRAY: "hover:bg-neutral-200",
AUTOTABLE_HEADER_COLOR_DARK_BLUE: "hover:bg-sky-100",
AUTOTABLE_HEADER_COLOR_DEFAULT: "hover:bg-surface-strong",
AUTOTABLE_HEADER_COLOR_BLUE: "hover:bg-sky-100 dark:bg-sky-900",
AUTOTABLE_HEADER_COLOR_GREEN: "hover:bg-green-100 dark:bg-green-900",
AUTOTABLE_HEADER_COLOR_GRAY: "hover:bg-surface-strong",
AUTOTABLE_HEADER_COLOR_DARK_BLUE: "hover:bg-sky-100 dark:bg-sky-900",
}
// POS_CLS is the text alignment per ColumnPosition.
@@ -160,7 +160,7 @@ var HEADER_INNER_POS = map[ColumnPosition]string{
// -- Class string constants for parts that don't vary by config ------------
const (
TBL_CONTAINER = "relative flex flex-col w-full h-full bg-white rounded-default overflow-hidden"
TBL_CONTAINER = "relative flex flex-col w-full h-full bg-surface rounded-default overflow-hidden"
TBL_WRAPPER = "overflow-x-auto w-full"
TBL_BASE = "min-w-full"
@@ -169,20 +169,20 @@ const (
atSortIconWrap = "leading-none shrink-0 opacity-50"
atSkeleton = "h-4 bg-neutral-200 rounded-default animate-pulse"
atErrorCell = "text-center text-red-600"
atEmptyCell = "text-center text-neutral-500"
atSkeleton = "h-4 bg-surface-strong rounded-default animate-pulse"
atErrorCell = "text-center text-red-600 dark:text-red-400"
atEmptyCell = "text-center text-ink-muted"
atPaginationBase = "flex justify-between items-center border-t border-neutral-300"
atPaginationInfo = "hidden sm:flex items-center text-sm text-neutral-500"
atPaginationBase = "flex justify-between items-center border-t border-line-strong"
atPaginationInfo = "hidden sm:flex items-center text-sm text-ink-muted"
atPaginationControls = "flex items-center"
atPaginationLabel = "hidden sm:block text-sm text-neutral-500 mr-2"
atPaginationPage = "text-sm text-neutral-500 px-3"
atPaginationBtn = "p-1 min-h-9 text-sm font-normal leading-none bg-transparent border-0 cursor-pointer hover:bg-neutral-100 disabled:text-neutral-300 disabled:cursor-not-allowed disabled:hover:bg-transparent"
atPaginationLabel = "hidden sm:block text-sm text-ink-muted mr-2"
atPaginationPage = "text-sm text-ink-muted px-3"
atPaginationBtn = "p-1 min-h-9 text-sm font-normal leading-none bg-transparent border-0 cursor-pointer hover:bg-surface-raised disabled:text-ink-faint disabled:cursor-not-allowed disabled:hover:bg-transparent"
// thead sticky classes are the STATIC replacement for the TSX's JS-driven
// header pinning (transform tracking on scroll). See file-level NOTE.
atTheadCls = "sticky top-0 z-10 [&_th]:border-b [&_th]:border-neutral-300"
atTheadCls = "sticky top-0 z-10 [&_th]:border-b [&_th]:border-line-strong"
)
// AutoTableColumn describes one column: its header, alignment, width, whether it
@@ -475,7 +475,7 @@ func AutoTable(cols []AutoTableColumn, rows []any, opts ...AutoTableOption) *vdo
containerCls := TBL_CONTAINER
if cfg.surroundingBorder {
containerCls = cx(containerCls, "border border-neutral-300")
containerCls = cx(containerCls, "border border-line-strong")
}
if cfg.shadow {
containerCls = cx(containerCls, "shadow-sm")
@@ -754,7 +754,7 @@ func atResizeHandle(key string, h *atColumnHooks) *vdom.VNode {
func atRenderBody(cols []AutoTableColumn, rows []any, cfg *atConfig) *vdom.VNode {
bodyCls := BODY_PADDING_CLS[cfg.size]
if cfg.borderY {
bodyCls = cx(bodyCls, "[&_td+td]:border-l [&_td+td]:border-neutral-300")
bodyCls = cx(bodyCls, "[&_td+td]:border-l [&_td+td]:border-line-strong")
}
colspan := atTotalColumns(cols, cfg)
@@ -768,7 +768,7 @@ func atRenderBody(cols []AutoTableColumn, rows []any, cfg *atConfig) *vdom.VNode
for r := 0; r < 5; r++ {
tr := vdom.Tr()
if cfg.alternate && r%2 == 1 {
tr.Attrs["class"] = "bg-neutral-100"
tr.Attrs["class"] = "bg-surface-raised"
}
if cfg.accordion {
tr.Children = append(tr.Children, vdom.Td(vdom.Attr("class", AUTOTABLE_ACCORDION_CELL)))
@@ -799,11 +799,11 @@ func atRenderBody(cols []AutoTableColumn, rows []any, cfg *atConfig) *vdom.VNode
isLast := rowIdx == len(rows)-1
rowCls := ""
if cfg.alternate && rowIdx%2 == 1 {
rowCls = cx(rowCls, "bg-neutral-100")
rowCls = cx(rowCls, "bg-surface-raised")
}
rowCls = cx(rowCls, hoverCls)
if cfg.borderX && !isLast {
rowCls = cx(rowCls, "border-b border-neutral-300")
rowCls = cx(rowCls, "border-b border-line-strong")
}
if cfg.highlight != nil && cfg.highlight(row) {
rowCls = cx(rowCls, AUTOTABLE_HIGHLIGHT_ROW)
@@ -2182,14 +2182,14 @@ func (s *AutoTableState) FiltersToggle() *vdom.VNode {
const (
AUTOTABLE_SEARCH_FIELDS = "flex flex-wrap items-end gap-2"
AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 grow sm:grow-0 sm:w-48"
AUTOTABLE_SEARCH_SELECT = "w-full rounded-default border border-neutral-300 bg-white px-3 py-2 text-sm"
AUTOTABLE_FILTERS_TOGGLE = "sm:hidden inline-flex items-center gap-2 rounded-default border border-neutral-300 px-3 py-2 text-sm"
AUTOTABLE_SEARCH_SELECT = "w-full rounded-default border border-line-strong bg-surface px-3 py-2 text-sm"
AUTOTABLE_FILTERS_TOGGLE = "sm:hidden inline-flex items-center gap-2 rounded-default border border-line-strong px-3 py-2 text-sm"
AUTOTABLE_FILTERS_BADGE = "ml-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-sky-600 px-1.5 text-xs font-semibold text-white"
AUTOTABLE_TOOLBAR = "flex flex-wrap items-end justify-between gap-3 pb-3"
AUTOTABLE_ASIDE = "w-full sm:w-64 shrink-0 rounded-default border border-neutral-300 bg-white p-3"
AUTOTABLE_ASIDE = "w-full sm:w-64 shrink-0 rounded-default border border-line-strong bg-surface p-3"
AUTOTABLE_ACCORDION_CELL = "w-10 text-center"
AUTOTABLE_ACCORDION_ROW = "bg-neutral-50"
AUTOTABLE_HIGHLIGHT_ROW = "bg-amber-100! shadow-[inset_3px_0_0_0_theme(colors.amber.500)]"
AUTOTABLE_ACCORDION_ROW = "bg-surface-muted"
AUTOTABLE_HIGHLIGHT_ROW = "bg-amber-100 dark:bg-amber-900! shadow-[inset_3px_0_0_0_theme(colors.amber.500)]"
)
// ==========================================================================
@@ -2602,7 +2602,7 @@ func (s *AutoTableState) pendingSkeleton() *vdom.VNode {
vdom.Attr("aria-label", "Loading table"),
vdom.Div(
kids([]vdom.Mod{
vdom.Attr("class", cx(TBL_CONTAINER, "border border-neutral-300 p-4 flex flex-col gap-3")),
vdom.Attr("class", cx(TBL_CONTAINER, "border border-line-strong p-4 flex flex-col gap-3")),
}, bars)...,
),
)
@@ -2718,6 +2718,7 @@ func (s *AutoTableState) ColumnPicker() *vdom.VNode {
if len(options) == 0 {
return nil
}
total := len(options)
return s.multiSelect("__columns__").Render(FormMultiSelectProps{
Options: options,
Value: selected,
@@ -2725,6 +2726,11 @@ func (s *AutoTableState) ColumnPicker() *vdom.VNode {
Searchable: true,
ShowSelectAll: true,
FieldWidth: "w-52",
// Not "5 items selected": this picker is about COLUMNS, and the count is only
// meaningful next to how many there are to choose from.
CollapsedLabel: func(n int) string {
return strconv.Itoa(n) + " of " + strconv.Itoa(total) + " columns"
},
OnChange: func(visible []string) {
show := map[string]bool{}
for _, k := range visible {
@@ -2779,7 +2785,7 @@ func (s *AutoTableState) ResetMenu() *vdom.VNode {
}
if len(items) == 0 {
items = append(items, vdom.Div(vdom.Attr("class", "px-3 py-2 text-sm text-neutral-500"),
items = append(items, vdom.Div(vdom.Attr("class", "px-3 py-2 text-sm text-ink-muted"),
vdom.Text("Nothing to reset"),
))
} else {
@@ -3091,7 +3097,7 @@ func (s *AutoTableState) summaryFoot(ctx *CalcContext, colCount int) *vdom.VNode
}
// AUTOTABLE_TFOOT styles the summary footer.
const AUTOTABLE_TFOOT = "border-t-2 border-neutral-300 bg-neutral-50 [&_td]:p-3"
const AUTOTABLE_TFOOT = "border-t-2 border-line-strong bg-surface-muted [&_td]:p-3"
// ---- the calculated-column editor ----
@@ -3127,16 +3133,16 @@ func isBinaryCalcFn(fn CalculatedFunction) bool {
// their metrics line up to the pixel; if they ever drift, the caret stops landing on
// the glyph it appears to be on.
const (
CALC_ADD_TRIGGER_CLS = "inline-flex items-center justify-center gap-2 cursor-pointer text-sm font-normal rounded-default transition shadow-xs bg-neutral-50 text-black border border-neutral-300 hover:bg-neutral-100 py-1 px-3"
CALC_ADD_TRIGGER_CLS = "inline-flex items-center justify-center gap-2 cursor-pointer text-sm font-normal rounded-default transition shadow-xs bg-surface-muted text-ink border border-line-strong hover:bg-surface-raised py-1 px-3"
FORMULA_EDIT_BASE = "block w-full h-[30px] font-mono text-sm p-1 rounded-default box-border whitespace-pre"
FORMULA_OVERLAY_CLS = FORMULA_EDIT_BASE + " absolute inset-0 overflow-hidden pointer-events-none border border-transparent text-neutral-800"
FORMULA_TEXTAREA_CLS = FORMULA_EDIT_BASE + " relative bg-transparent text-transparent caret-neutral-800 resize-none overflow-x-auto overflow-y-hidden shadow-xs border border-neutral-300 focus:border-sky-500 outline-hidden"
FORMULA_OVERLAY_CLS = FORMULA_EDIT_BASE + " absolute inset-0 overflow-hidden pointer-events-none border border-transparent text-ink"
FORMULA_TEXTAREA_CLS = FORMULA_EDIT_BASE + " relative bg-transparent text-transparent caret-neutral-800 resize-none overflow-x-auto overflow-y-hidden shadow-xs border border-line-strong focus:border-sky-500 outline-hidden"
formulaPlaceholder = `<span class="text-neutral-400">e.g. [Revenue] / SUM({Revenue}) * 100</span>`
formulaPlaceholder = `<span class="text-ink-faint">e.g. [Revenue] / SUM({Revenue}) * 100</span>`
calcMenuTrigger = "inline-flex items-center gap-1 rounded-default border border-neutral-300 bg-neutral-50 px-2 py-1 text-xs leading-none text-neutral-700 cursor-pointer hover:bg-neutral-100"
calcChooserItem = "w-full text-left px-2 py-2 rounded-default hover:bg-neutral-100 cursor-pointer border-0 bg-transparent flex items-start gap-2.5"
calcMenuTrigger = "inline-flex items-center gap-1 rounded-default border border-line-strong bg-surface-muted px-2 py-1 text-xs leading-none text-ink-soft cursor-pointer hover:bg-surface-raised"
calcChooserItem = "w-full text-left px-2 py-2 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-start gap-2.5"
)
// HighlightFormula renders a formula as HTML with syntax colouring: cell refs
@@ -3145,6 +3151,20 @@ const (
// Purely visual, and deliberately LEXICAL rather than a real parse — it has to
// colour a half-typed formula that does not compile yet, which is exactly when the
// colours are worth the most.
// The formula highlighter's token colours.
//
// Named, rather than written inline, because they are asserted on in tests — and a
// colour spelled out in two places is a colour that gets changed in one. (It already
// happened: adding the dark: variants broke every highlighter test, none of which cared
// about the colour, all of which had a copy of it.)
const (
fmlCellClass = "text-sky-600 dark:text-sky-400" // [Cell]
fmlColumnClass = "text-violet-600 dark:text-violet-400" // {Column}
fmlNumberClass = "text-amber-600 dark:text-amber-400" // 42, PI
fmlFuncClass = "text-emerald-700 dark:text-emerald-400 font-semibold" // SUM(
fmlOpClass = "text-ink-faint" // + - * /
)
func HighlightFormula(src string) string {
var b strings.Builder
i := 0
@@ -3159,7 +3179,7 @@ func HighlightFormula(src string) string {
if j >= 0 {
end = i + j + 1
}
b.WriteString(`<span class="text-sky-600">`)
b.WriteString(`<span class="` + fmlCellClass + `">`)
b.WriteString(html.EscapeString(src[i:end]))
b.WriteString(`</span>`)
i = end
@@ -3176,7 +3196,7 @@ func HighlightFormula(src string) string {
}
j++
}
b.WriteString(`<span class="text-violet-600">`)
b.WriteString(`<span class="` + fmlColumnClass + `">`)
b.WriteString(html.EscapeString(src[i:j]))
b.WriteString(`</span>`)
i = j
@@ -3186,7 +3206,7 @@ func HighlightFormula(src string) string {
for j < len(src) && (isDigit(src[j]) || src[j] == '.') {
j++
}
b.WriteString(`<span class="text-amber-600">`)
b.WriteString(`<span class="` + fmlNumberClass + `">`)
b.WriteString(html.EscapeString(src[i:j]))
b.WriteString(`</span>`)
i = j
@@ -3205,11 +3225,11 @@ func HighlightFormula(src string) string {
}
switch {
case k < len(src) && src[k] == '(':
b.WriteString(`<span class="text-emerald-700 font-semibold">`)
b.WriteString(`<span class="` + fmlFuncClass + `">`)
b.WriteString(html.EscapeString(word))
b.WriteString(`</span>`)
case isFormulaConstant(word):
b.WriteString(`<span class="text-amber-600">`)
b.WriteString(`<span class="` + fmlNumberClass + `">`)
b.WriteString(html.EscapeString(word))
b.WriteString(`</span>`)
default:
@@ -3218,7 +3238,7 @@ func HighlightFormula(src string) string {
i = j
case strings.IndexByte("+-*/^%=<>(),:", c) >= 0:
b.WriteString(`<span class="text-neutral-400">`)
b.WriteString(`<span class="` + fmlOpClass + `">`)
b.WriteString(html.EscapeString(string(c)))
b.WriteString(`</span>`)
i++
@@ -3590,7 +3610,7 @@ func (s *AutoTableState) calcChooser(e *calcEditor) *vdom.VNode {
))
}
if len(existing) > 0 {
children = append(children, vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1 border-b border-neutral-200 pb-2 mb-1")}, existing)...))
children = append(children, vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1 border-b border-line pb-2 mb-1")}, existing)...))
}
children = append(children,
@@ -3607,19 +3627,19 @@ func calcChooserButton(icon, title, subtitle string, onClick func()) *vdom.VNode
return vdom.Button(vdom.Attr("type", "button"),
vdom.Attr("class", calcChooserItem),
vdom.On(vdom.EVENT_CLICK, onClick),
Icon(icon, 16, "mt-0.5 text-neutral-500"),
Icon(icon, 16, "mt-0.5 text-ink-muted"),
vdom.Span(vdom.Attr("class", "flex flex-col"),
vdom.Span(vdom.Attr("class", "text-sm font-medium text-neutral-800"), vdom.Text(title)),
vdom.Span(vdom.Attr("class", "text-xs text-neutral-500"), vdom.Text(subtitle)),
vdom.Span(vdom.Attr("class", "text-sm font-medium text-ink"), vdom.Text(title)),
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(subtitle)),
),
)
}
func calcListRow(title, subtitle string, onEdit, onDelete func()) *vdom.VNode {
return vdom.Div(vdom.Attr("class", "flex items-center gap-2 rounded-default px-2 py-1 hover:bg-neutral-50"),
return vdom.Div(vdom.Attr("class", "flex items-center gap-2 rounded-default px-2 py-1 hover:bg-surface-muted"),
vdom.Div(vdom.Attr("class", "min-w-0 grow"),
vdom.Div(vdom.Attr("class", "truncate text-sm font-medium text-neutral-800"), vdom.Text(title)),
vdom.Div(vdom.Attr("class", "truncate font-mono text-xs text-neutral-500"), vdom.Text(subtitle)),
vdom.Div(vdom.Attr("class", "truncate text-sm font-medium text-ink"), vdom.Text(title)),
vdom.Div(vdom.Attr("class", "truncate font-mono text-xs text-ink-muted"), vdom.Text(subtitle)),
),
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "pencil", Title: "Edit", OnClick: onEdit}),
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "trash", Title: "Delete", OnClick: onDelete}),
@@ -3712,14 +3732,14 @@ func (s *AutoTableState) calcForm(e *calcEditor, summary bool) *vdom.VNode {
title = "Edit " + strings.ToLower(title)
}
header := vdom.Div(vdom.Attr("class", "flex items-center gap-2 border-b border-neutral-200 px-3 py-2"),
header := vdom.Div(vdom.Attr("class", "flex items-center gap-2 border-b border-line px-3 py-2"),
vdom.Button(vdom.Attr("type", "button"),
vdom.Attr("class", "shrink-0 cursor-pointer rounded-default p-1 text-neutral-500 hover:bg-neutral-100 border-0 bg-transparent"),
vdom.Attr("class", "shrink-0 cursor-pointer rounded-default p-1 text-ink-muted hover:bg-surface-raised border-0 bg-transparent"),
vdom.Attr("aria-label", "Back"),
vdom.On(vdom.EVENT_CLICK, e.reset),
Icon("chevron-left", 14, ""),
),
vdom.Span(vdom.Attr("class", "text-sm font-medium text-neutral-800"), vdom.Text(title)),
vdom.Span(vdom.Attr("class", "text-sm font-medium text-ink"), vdom.Text(title)),
// Basic vs Advanced.
vdom.Div(vdom.Attr("class", "ml-auto"),
SegmentedButtons([]SegmentedButtonOption{
@@ -3773,7 +3793,7 @@ func (s *AutoTableState) calcForm(e *calcEditor, summary bool) *vdom.VNode {
}
if msg := e.errorMsg.Get(); msg != "" {
fields = append(fields, vdom.P(vdom.Attr("class", "text-xs text-red-600"), vdom.Text(msg)))
fields = append(fields, vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(msg)))
}
saveLabel := "Add"
@@ -3877,7 +3897,7 @@ func (s *AutoTableState) calcBasicEditor(e *calcEditor, summary bool) *vdom.VNod
})))
}
children = append(children, vdom.P(vdom.Attr("class", "text-xs text-neutral-500"),
children = append(children, vdom.P(vdom.Attr("class", "text-xs text-ink-muted"),
vdom.Text(calcBasicHint(e, summary))))
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-2")}, children)...)
@@ -3908,12 +3928,12 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
label := o.Label
colItems = append(colItems,
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("[" + label + "]") }},
vdom.Span(vdom.Attr("class", "font-mono text-sky-600"), vdom.Text("["+label+"]")),
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-neutral-500"), vdom.Text("this row")),
vdom.Span(vdom.Attr("class", "font-mono text-sky-600 dark:text-sky-400"), vdom.Text("["+label+"]")),
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-ink-muted"), vdom.Text("this row")),
),
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("{" + label + "}") }},
vdom.Span(vdom.Attr("class", "font-mono text-violet-600"), vdom.Text("{"+label+"}")),
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-neutral-500"), vdom.Text("whole column")),
vdom.Span(vdom.Attr("class", "font-mono text-violet-600 dark:text-violet-400"), vdom.Text("{"+label+"}")),
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-ink-muted"), vdom.Text("whole column")),
),
)
}
@@ -3925,7 +3945,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
groups := filterFormulaGroups(e.fnSearch.Get())
if len(groups) == 0 {
fnItems = append(fnItems, vdom.Div(
vdom.Attr("class", "px-2 py-3 text-center text-xs text-neutral-500"),
vdom.Attr("class", "px-2 py-3 text-center text-xs text-ink-muted"),
vdom.Text("No functions match"),
))
}
@@ -3940,7 +3960,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
// The signature is syntax-highlighted with the same highlighter the
// formula box uses, so the menu and the editor speak one language.
vdom.Span(vdom.Attr("class", "font-mono text-xs"), vdom.Raw(HighlightFormula(f.Sig))),
vdom.Span(vdom.Attr("class", "text-xs text-neutral-500"), vdom.Text(f.Desc)),
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(f.Desc)),
),
))
}
@@ -3953,8 +3973,8 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
OnClick: func() { e.insertAtCaret(cc.Name) },
},
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5"),
vdom.Span(vdom.Attr("class", "font-mono text-xs text-amber-600"), vdom.Text(cc.Name)),
vdom.Span(vdom.Attr("class", "text-xs text-neutral-500"), vdom.Text(cc.Desc)),
vdom.Span(vdom.Attr("class", "font-mono text-xs text-amber-600 dark:text-amber-400"), vdom.Text(cc.Name)),
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(cc.Desc)),
),
))
}
@@ -4008,7 +4028,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
),
)
help := vdom.P(vdom.Attr("class", "text-xs text-neutral-500"),
help := vdom.P(vdom.Attr("class", "text-xs text-ink-muted"),
vdom.Text("[Column] is this row's cell · {Column} is the whole column · {Column:1:ROW()} is a running total."))
children := []*vdom.VNode{menus, editor, help}
@@ -4024,11 +4044,11 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
func (e *calcEditor) fnSearchBox() *vdom.VNode {
wasmruntime.AfterRender(func() { wasmruntime.Focus(e.fnSearchRef) })
return vdom.Div(vdom.Attr("class", "sticky top-0 z-10 -m-1.5 mb-1 border-b border-neutral-200 bg-white p-1.5"),
return vdom.Div(vdom.Attr("class", "sticky top-0 z-10 -m-1.5 mb-1 border-b border-line bg-surface p-1.5"),
vdom.Input(
vdom.WithRef(e.fnSearchRef),
vdom.Attr("type", "text"),
vdom.Attr("class", "w-full rounded-default border border-neutral-300 p-1 text-xs focus:outline-2 focus:outline-sky-500"),
vdom.Attr("class", "w-full rounded-default border border-line-strong p-1 text-xs focus:outline-2 focus:outline-sky-500"),
vdom.Attr("placeholder", "Search functions…"),
vdom.Attr("spellcheck", "false"),
vdom.Prop("value", e.fnSearch.Get()),
@@ -4069,12 +4089,12 @@ func (s *AutoTableState) calcPreview(e *calcEditor, src string, summary bool) *v
return nil
}
if _, err := CompileFormula(src); err != nil {
return vdom.P(vdom.Attr("class", "text-xs text-red-600"), vdom.Text(err.Error()))
return vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
}
rows := s.FilteredRows()
if len(rows) == 0 {
return vdom.P(vdom.Attr("class", "text-xs text-neutral-500"), vdom.Text("Formula is valid."))
return vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text("Formula is valid."))
}
ctx := NewCalcContext(rows, s.cols, s.Calculated(), s.read)
@@ -4087,10 +4107,10 @@ func (s *AutoTableState) calcPreview(e *calcEditor, src string, summary bool) *v
n, err := EvalFormula(src, target)
if err != nil {
return vdom.P(vdom.Attr("class", "text-xs text-red-600"), vdom.Text(err.Error()))
return vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
}
out := FormatCalcResult(n, CalculatedDataType(e.dataType.Get()), calcPrecisionOf(e.precision.Get()), "", "", "")
return vdom.P(vdom.Attr("class", "text-xs text-neutral-500"), vdom.Text(prefix+out))
return vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(prefix+out))
}
// insertAtCaret drops text where the cursor is, rather than at the end — which is
@@ -4121,7 +4141,7 @@ func (e *calcEditor) insertAtCaretOffset(text string, back int) {
func calcField(label string, children ...*vdom.VNode) *vdom.VNode {
nodes := []*vdom.VNode{
vdom.Span(vdom.Attr("class", "text-xs font-medium text-neutral-600"), vdom.Text(label)),
vdom.Span(vdom.Attr("class", "text-xs font-medium text-ink-soft"), vdom.Text(label)),
}
nodes = append(nodes, children...)
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1")}, nodes)...)