Chat Messages: components 201-220
This commit is contained in:
121 files changed
+5737
-1
No files matched your search
+125
@@ -0,0 +1,125 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"mujicaui/icons"
|
||||
"mujicaui/internal"
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// TableBlockOptions configure a TableBlock.
|
||||
type TableBlockOptions struct {
|
||||
// Align sets each column's alignment: ui.Start, ui.Center or ui.End; missing columns start.
|
||||
Align []ui.Align
|
||||
}
|
||||
|
||||
// TableBlock shows a small table inside a message; it scrolls sideways and copies as TSV.
|
||||
func TableBlock(c *ui.Context, header []string, rows [][]string, opts TableBlockOptions) CopyResult {
|
||||
n := len(header)
|
||||
if n == 0 {
|
||||
panic("mujica: TableBlock needs a header")
|
||||
}
|
||||
if len(opts.Align) > n {
|
||||
panic("mujica: TableBlock has more alignments than columns")
|
||||
}
|
||||
for _, row := range rows {
|
||||
if len(row) != n {
|
||||
panic("mujica: TableBlock row width differs from the header")
|
||||
}
|
||||
}
|
||||
k := Tokens(c)
|
||||
u := Density(c).Unit()
|
||||
var r CopyResult
|
||||
r.Element = ui.Column(c).Radius(theme.CardRadius).Border(theme.BorderWidth, k.Border).Background(k.Surface).Clip().MinWidth(0)
|
||||
copiedAt := ui.Local(r.Element, "copied", func() time.Time { return time.Time{} })
|
||||
r.Element.Children(func() {
|
||||
ui.Row(c).Padding(0, u, 0, u*3).AlignItems(ui.Center).Background(k.SurfaceHover).Children(func() {
|
||||
ui.Text(c, msg(c, "table_block.title")).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).Grow(1)
|
||||
icon, name := "copy", msg(c, "table_block.copy")
|
||||
if left := codeBlockCopiedFor - c.Now().Sub(*copiedAt); left > 0 {
|
||||
icon, name = "check", msg(c, "code_block.copied")
|
||||
c.After(left)
|
||||
}
|
||||
if iconAction(c, icons.Must(icon), name).Clicked() && !r.Element.IsDisabled() {
|
||||
c.WriteClipboard(tableBlockTSV(header, rows))
|
||||
*copiedAt, r.copied = c.Now(), true
|
||||
c.Announce(msg(c, "code_block.copied"))
|
||||
slog.Debug("mujica: table_block copy", "rows", len(rows))
|
||||
}
|
||||
})
|
||||
tracks := make([]ui.Track, n)
|
||||
for i := range tracks {
|
||||
tracks[i] = ui.FitContent()
|
||||
}
|
||||
sc := ui.ScrollHorizontal(c).AlignItems(ui.Start)
|
||||
// Rows span the block: the grid is at least last frame's scroll width.
|
||||
w := sc.Bounds().W
|
||||
internal.Remeasure(c, sc, w, sc.Bounds().H)
|
||||
sc.Children(func() {
|
||||
ui.Grid(c).ColumnTracks(tracks...).MinWidth(w).Children(func() {
|
||||
for i, h := range header {
|
||||
tableBlockCell(c, h, tableBlockAlign(opts.Align, i), true, false)
|
||||
}
|
||||
for ri, row := range rows {
|
||||
for i, cell := range row {
|
||||
tableBlockCell(c, cell, tableBlockAlign(opts.Align, i), false, ri == len(rows)-1)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func tableBlockAlign(a []ui.Align, i int) ui.Align {
|
||||
if i < len(a) {
|
||||
return a[i]
|
||||
}
|
||||
return ui.Start
|
||||
}
|
||||
|
||||
func tableBlockCell(c *ui.Context, s string, align ui.Align, head, last bool) {
|
||||
k := Tokens(c)
|
||||
u := Density(c).Unit()
|
||||
cell := ui.Box(c).Padding(u*2, u*3).Role(ui.RoleCell)
|
||||
if !last {
|
||||
cell.BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border)
|
||||
}
|
||||
if head {
|
||||
cell.Role(ui.RoleColumnHeader)
|
||||
}
|
||||
cell.Children(func() {
|
||||
t := ui.Text(c, tableBlockClean.Replace(s)).NoWrap().TextAlign(align)
|
||||
if head {
|
||||
t.FontWeight(600)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// tableBlockClean turns a cell's tabs and newlines into spaces.
|
||||
var tableBlockClean = strings.NewReplacer("\t", " ", "\r\n", " ", "\n", " ")
|
||||
|
||||
// tableBlockTSV joins cells with tabs and rows with newlines.
|
||||
func tableBlockTSV(header []string, rows [][]string) string {
|
||||
clean := tableBlockClean
|
||||
var b strings.Builder
|
||||
line := func(cells []string) {
|
||||
for i, s := range cells {
|
||||
if i > 0 {
|
||||
b.WriteByte('\t')
|
||||
}
|
||||
b.WriteString(clean.Replace(s))
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
line(header)
|
||||
for _, row := range rows {
|
||||
line(row)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
Reference in new issue
Block a user