Data Table Page
A page frame for a server-driven data table.
<datatablepage.DataTablePage title="Invoices" description="Every invoice for this workspace.">
<:filter>
<input.Input name="q" type="search" placeholder="Search" attrs={gx.Attrs{{Key: "aria-label", Value: "Search"}}} />
</:filter>
<:table>
<table.Table>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
<table.TableHead class="text-right">Amount</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow>
<table.TableCell class="font-medium">INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell class="text-right">$120.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell class="font-medium">INV-002</table.TableCell>
<table.TableCell>Open</table.TableCell>
<table.TableCell class="text-right">$80.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell class="font-medium">INV-003</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell class="text-right">$310.00</table.TableCell>
</table.TableRow>
</table.TableBody>
</table.Table>
</:table>
<:pagination>
<pagination.Pagination>
<pagination.PaginationContent>
<pagination.PaginationItem>
<pagination.PaginationPrevious href={gx.URL("/invoices?page=1")} />
</pagination.PaginationItem>
<pagination.PaginationItem>
<pagination.PaginationLink href={gx.URL("/invoices?page=1")} active>1</pagination.PaginationLink>
</pagination.PaginationItem>
<pagination.PaginationItem>
<pagination.PaginationLink href={gx.URL("/invoices?page=2")}>2</pagination.PaginationLink>
</pagination.PaginationItem>
<pagination.PaginationItem>
<pagination.PaginationNext href={gx.URL("/invoices?page=2")} />
</pagination.PaginationItem>
</pagination.PaginationContent>
</pagination.Pagination>
</:pagination>
</datatablepage.DataTablePage>Installation
Run the command in the app module.
gx add data-table-pageThe command also installs card, input, pagination and table.
The command writes these files.
| File | Path in the app |
|---|---|
DataTablePage.gx |
ui/data-table-page/DataTablePage.gx |
DataTablePage.fixtures.go |
ui/data-table-page/DataTablePage.fixtures.go |
Install card, input, pagination and table first.
Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.
package datatablepage
import "github.com/alternayte/gx/registry/card"
import "github.com/alternayte/gx/registry/input"
props {
// Title is the heading of the page.
Title string
// Description is the text below the title. An empty value renders no description.
Description string = ""
// Filter is the filter control shown in the header row. A nil value renders no filter.
Filter gx.Node = nil
// Table is the data table. It renders in a card.
Table gx.Node
// Pagination is the paging links shown below the table. A nil value renders no paging.
Pagination gx.Node = nil
}
<div class="mx-auto flex w-full max-w-5xl flex-col gap-6">
<div class="flex flex-wrap items-end justify-between gap-4">
<div class="flex flex-col gap-1">
<h1 class="text-xl font-semibold">{p.Title}</h1>
if p.Description != "" {
<p class="text-sm text-muted-foreground">{p.Description}</p>
}
</div>
if p.Filter != nil {
<div class="w-56">
{p.Filter}
</div>
}
</div>
<card.Card>
{p.Table}
</card.Card>
if p.Pagination != nil {
<div class="flex justify-center">{p.Pagination}</div>
}
</div>package datatablepage
import (
"github.com/alternayte/gx"
"github.com/alternayte/gx/registry/input"
"github.com/alternayte/gx/registry/pagination"
"github.com/alternayte/gx/registry/table"
)
// row renders one invoice row of the fixture table.
func row(id, status, amount string) gx.Node {
return table.TableRow(table.TableRowProps{Children: gx.Frag(
table.TableCell(table.TableCellProps{Class: "font-medium", Children: gx.Text(id)}),
table.TableCell(table.TableCellProps{Children: gx.Text(status)}),
table.TableCell(table.TableCellProps{Class: "text-right", Children: gx.Text(amount)}),
)})
}
var DataTablePageFixtures = gx.Fixtures[DataTablePageProps]{
"Default": {
Title: "Invoices",
Description: "Every invoice for this workspace.",
Filter: input.Input(input.InputProps{Name: "q", Type: "search", Placeholder: "Search", Attrs: gx.Attrs{{Key: "aria-label", Value: "Search"}}}),
Table: table.Table(table.TableProps{Children: gx.Frag(
table.TableHeader(table.TableHeaderProps{Children: table.TableRow(table.TableRowProps{Children: gx.Frag(
table.TableHead(table.TableHeadProps{Children: gx.Text("Invoice")}),
table.TableHead(table.TableHeadProps{Children: gx.Text("Status")}),
table.TableHead(table.TableHeadProps{Class: "text-right", Children: gx.Text("Amount")}),
)})}),
table.TableBody(table.TableBodyProps{Children: gx.Frag(
row("INV-001", "Paid", "$120.00"),
row("INV-002", "Open", "$80.00"),
row("INV-003", "Paid", "$310.00"),
)}),
)}),
Pagination: pagination.Pagination(pagination.PaginationProps{Children: pagination.PaginationContent(pagination.PaginationContentProps{Children: gx.Frag(
pagination.PaginationItem(pagination.PaginationItemProps{Children: pagination.PaginationPrevious(pagination.PaginationPreviousProps{Href: gx.URL("/invoices?page=1")})}),
pagination.PaginationItem(pagination.PaginationItemProps{Children: pagination.PaginationLink(pagination.PaginationLinkProps{Href: gx.URL("/invoices?page=1"), Active: true, Children: gx.Text("1")})}),
pagination.PaginationItem(pagination.PaginationItemProps{Children: pagination.PaginationLink(pagination.PaginationLinkProps{Href: gx.URL("/invoices?page=2"), Children: gx.Text("2")})}),
pagination.PaginationItem(pagination.PaginationItemProps{Children: pagination.PaginationNext(pagination.PaginationNextProps{Href: gx.URL("/invoices?page=2")})}),
)})}),
},
}The theme must define these tokens: --border, --card, --card-foreground, --muted-foreground.
Usage
<datatablepage.DataTablePage title="Invoices" filter={filterInput} table={table} pagination={paging} />The block is copied source. The table and the paging links come from the app, so the sort, filter and page state stay typed in Go.
Examples
Default
<datatablepage.DataTablePage title="Invoices" description="Every invoice for this workspace.">
<:filter>
<input.Input name="q" type="search" placeholder="Search" attrs={gx.Attrs{{Key: "aria-label", Value: "Search"}}} />
</:filter>
<:table>
<table.Table>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
<table.TableHead class="text-right">Amount</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow>
<table.TableCell class="font-medium">INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell class="text-right">$120.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell class="font-medium">INV-002</table.TableCell>
<table.TableCell>Open</table.TableCell>
<table.TableCell class="text-right">$80.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell class="font-medium">INV-003</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell class="text-right">$310.00</table.TableCell>
</table.TableRow>
</table.TableBody>
</table.Table>
</:table>
<:pagination>
<pagination.Pagination>
<pagination.PaginationContent>
<pagination.PaginationItem>
<pagination.PaginationPrevious href={gx.URL("/invoices?page=1")} />
</pagination.PaginationItem>
<pagination.PaginationItem>
<pagination.PaginationLink href={gx.URL("/invoices?page=1")} active>1</pagination.PaginationLink>
</pagination.PaginationItem>
<pagination.PaginationItem>
<pagination.PaginationLink href={gx.URL("/invoices?page=2")}>2</pagination.PaginationLink>
</pagination.PaginationItem>
<pagination.PaginationItem>
<pagination.PaginationNext href={gx.URL("/invoices?page=2")} />
</pagination.PaginationItem>
</pagination.PaginationContent>
</pagination.Pagination>
</:pagination>
</datatablepage.DataTablePage>API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
datatablepage.DataTablePage
| Prop | Type | Default | Description |
|---|---|---|---|
Title |
string |
Required | Title is the heading of the page. |
Description |
string |
"" |
Description is the text below the title. An empty value renders no description. |
Filter |
gx.Node |
nil |
Filter is the filter control shown in the header row. A nil value renders no filter. |
Table |
gx.Node |
Required | Table is the data table. It renders in a card. |
Pagination |
gx.Node |
nil |
Pagination is the paging links shown below the table. A nil value renders no paging. |
Do and do not
Do
- Put the filter in the header row.
- Keep the table in one card.
Don't
- Do not page on the client.
- Do not render more rows than the page size.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves to the filter, then the table links, then the paging links. |
| Enter | Follows the focused sort or page link. |