Search Apps Documentation Source Content File Folder Download Copy Actions Download

render.gno

2.82 Kb · 113 lines
  1package tablesort
  2
  3import (
  4	"net/url"
  5	"strings"
  6
  7	"gno.land/p/mason/md"
  8	"gno.land/p/nt/ufmt"
  9)
 10
 11// Table holds the headings and rows for rendering.
 12// Each row must have the same number of cells as there are headings.
 13type Table struct {
 14	Headings []string   // ["A", "B", "C"]
 15	Rows     [][]string // [["a1","b1","c1"], ["a2","b2","c2"], ...]
 16}
 17
 18// Render generates a Markdown table from a Table struct with sortable columns based on URL params.
 19// paramPrefix is an optional prefix for in the URL to identify the tablesort Renders (e.g. "members-").
 20func Render(u *url.URL, table *Table, paramPrefix string) string {
 21	direction := ""
 22	currentHeading := ""
 23	if h := u.Query().Get(paramPrefix + "sort-asc"); h != "" {
 24		direction = "asc"
 25		currentHeading = h
 26	} else if h := u.Query().Get(paramPrefix + "sort-desc"); h != "" {
 27		direction = "desc"
 28		currentHeading = h
 29	}
 30
 31	var sb strings.Builder
 32
 33	// Find the index of the column to sort
 34	colIndex := -1
 35	for i, h := range table.Headings {
 36		if h == currentHeading {
 37			colIndex = i
 38			break
 39		}
 40	}
 41
 42	// Sort rows if necessary
 43	if colIndex != -1 {
 44		SortRows(table.Rows, colIndex, direction == "asc")
 45	}
 46
 47	// Build header
 48	sb.WriteString(buildHeader(u, table.Headings, currentHeading, direction, paramPrefix))
 49	sb.WriteString("\n")
 50
 51	numCols := len(table.Headings)
 52
 53	// Build rows
 54	for i, row := range table.Rows {
 55		// Validate row length
 56		if len(row) != numCols {
 57			return "tablesort fails: row " + ufmt.Sprintf("%d", i+1) + " has " +
 58				ufmt.Sprintf("%d", len(row)) + " cells, expected " +
 59				ufmt.Sprintf("%d", numCols) + ", because there are " + ufmt.Sprintf("%d", numCols) + " columns.\n"
 60		}
 61
 62		sb.WriteString("|")
 63		for _, cell := range row {
 64			sb.WriteString(" " + cell + " |")
 65		}
 66		sb.WriteString("\n")
 67	}
 68
 69	return sb.String()
 70}
 71
 72// buildHeader builds the Markdown header row with clickable links and arrows
 73func buildHeader(u *url.URL, headings []string, currentHeading, direction string, paramPrefix string) string {
 74	var sb strings.Builder
 75	sb.WriteString("|")
 76	for _, h := range headings {
 77		arrow := ""
 78		if h == currentHeading {
 79			if direction == "asc" {
 80				arrow = " ↑"
 81			} else if direction == "desc" {
 82				arrow = " ↓"
 83			}
 84		}
 85
 86		// Build URL for the header link with toggle logic
 87		newURL := *u
 88		q := newURL.Query()
 89		if h == currentHeading {
 90			// Toggle sort direction
 91			if direction == "asc" {
 92				q.Del(paramPrefix + "sort-asc")
 93				q.Set(paramPrefix+"sort-desc", h)
 94			} else {
 95				q.Del(paramPrefix + "sort-desc")
 96				q.Set(paramPrefix+"sort-asc", h)
 97			}
 98		} else {
 99			// First click defaults to descending
100			q.Del(paramPrefix + "sort-asc")
101			q.Set(paramPrefix+"sort-desc", h)
102		}
103		newURL.RawQuery = q.Encode()
104		link := md.Link(h+arrow, newURL.String())
105		sb.WriteString(" " + link + " |")
106	}
107
108	sb.WriteString("\n|")
109	for range headings {
110		sb.WriteString(" --- |")
111	}
112	return sb.String()
113}