Tables & Data Layout
Creating structured data layouts with Sone's table components
Tables & Data Layout
Sone provides a comprehensive table system for creating structured data layouts with Table
, TableRow
, and TableCell
components.
Basic Table Structure
import { Table, TableRow, TableCell, Text, TextDefault } from "sone";
Table(
TableRow(
TableCell(Text("Name").weight("bold")),
TableCell(Text("Age").weight("bold")),
TableCell(Text("City").weight("bold"))
),
TableRow(
TableCell(Text("John Doe")),
TableCell(Text("30")),
TableCell(Text("New York"))
),
TableRow(
TableCell(Text("Jane Smith")),
TableCell(Text("25")),
TableCell(Text("Los Angeles"))
)
)
.borderWidth(1)
.borderColor("gray")
Table Components
Table
The root container for table layouts:
Table(
// TableRow components here
)
.spacing(10, 5) // Column spacing, row spacing
.padding(20) // Overall table padding
.bg("white") // Table background
.borderWidth(1) // Table border
.borderColor("black")
TableRow
Represents a table row:
TableRow(
// TableCell components here
)
.bg("lightgray") // Row background
.padding(5) // Row padding
.borderBottomWidth(1) // Bottom border
.borderColor("gray")
TableCell
Individual table cells:
TableCell(
Text("Cell content")
)
.padding(10) // Cell padding
.bg("white") // Cell background
.borderWidth(1) // Cell border
.borderColor("gray")
.width(100) // Fixed cell width
Cell Content
Text Content
TableCell(
Text("Simple text")
.size(14)
.color("black")
)
Rich Text in Cells
TableCell(
Text(
"Price: ",
Span("$299.99").color("green").weight("bold")
)
.size(16)
)
Multiple Elements
TableCell(
Column(
Text("Product Name").size(16).weight("bold"),
Text("Description text").size(12).color("gray")
)
)
.padding(15)
Using TextDefault for Cell Styling
TableRow(
TextDefault(
TableCell(Text("Column 1")),
TableCell(Text("Column 2")),
TableCell(Text("Column 3"))
)
.size(14) // Apply to all cell text
.color("black") // Apply to all cell text
.font("Arial") // Apply to all cell text
)
Table Styling
Alternating Row Colors
function StripedTable(data) {
return Table(
// Header row
TableRow(
...data.headers.map(header =>
TableCell(Text(header).weight("bold"))
.bg("#f0f0f0")
.padding(12)
)
),
// Data rows with alternating colors
...data.rows.map((row, index) =>
TableRow(
...row.map(cell =>
TableCell(Text(cell))
.padding(12)
)
)
.bg(index % 2 === 0 ? "white" : "#f9f9f9")
)
)
.borderWidth(1)
.borderColor("#ddd");
}
Bordered Table
function BorderedTable(data) {
return Table(
...data.map(row =>
TableRow(
...row.map(cell =>
TableCell(Text(cell))
.padding(10)
.borderWidth(1)
.borderColor("#ccc")
)
)
)
)
.borderWidth(2)
.borderColor("black");
}
Modern Table Design
function ModernTable(headers, rows) {
return Table(
// Header
TableRow(
...headers.map(header =>
TableCell(
Text(header)
.size(14)
.weight("600")
.color("white")
)
.padding(15, 20)
.bg("linear-gradient(135deg, #667eea 0%, #764ba2 100%)")
)
),
// Rows
...rows.map((row, index) =>
TableRow(
...row.map(cell =>
TableCell(Text(cell).size(14))
.padding(12, 20)
.borderBottomWidth(1)
.borderColor("#eee")
)
)
.bg(index % 2 === 0 ? "white" : "#f8f9fa")
)
)
.rounded(8)
.shadow("0 2px 4px rgba(0,0,0,0.1)")
.overflow("hidden");
}
Advanced Table Features
Column Spacing
Control spacing between columns:
Table(rows)
.spacing(20) // 20px between columns
.spacing(15, 10) // 15px between columns, 10px between rows
Fixed Column Widths
TableRow(
TableCell(Text("Fixed 100px")).width(100),
TableCell(Text("Fixed 200px")).width(200),
TableCell(Text("Flexible")).flex(1)
)
Cell Alignment
TableRow(
TableCell(Text("Left")).alignSelf("flex-start"),
TableCell(Text("Center")).alignSelf("center"),
TableCell(Text("Right")).alignSelf("flex-end")
)
.height(60)
Practical Examples
Data Table
function DataTable(title, data) {
return Column(
// Title
Text(title)
.size(20)
.weight("bold")
.marginBottom(15),
// Table
Table(
// Header
TableRow(
...Object.keys(data[0]).map(key =>
TableCell(
Text(key.charAt(0).toUpperCase() + key.slice(1))
.weight("bold")
.size(14)
)
.padding(12)
.bg("#f5f5f5")
.borderBottomWidth(2)
.borderColor("#ddd")
)
),
// Data rows
...data.map(row =>
TableRow(
...Object.values(row).map(value =>
TableCell(Text(String(value)).size(13))
.padding(12)
.borderBottomWidth(1)
.borderColor("#eee")
)
)
)
)
.borderWidth(1)
.borderColor("#ddd")
.rounded(6)
);
}
Invoice Table
function InvoiceTable(items) {
const subtotal = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const tax = subtotal * 0.1;
const total = subtotal + tax;
return Column(
// Items table
Table(
// Header
TableRow(
TableCell(Text("Description").weight("bold")),
TableCell(Text("Qty").weight("bold").align("center")),
TableCell(Text("Price").weight("bold").align("right")),
TableCell(Text("Amount").weight("bold").align("right"))
)
.bg("#f0f0f0")
.borderBottomWidth(2)
.borderColor("black"),
// Items
...items.map(item =>
TableRow(
TableCell(Text(item.description)),
TableCell(Text(item.quantity.toString()).align("center")),
TableCell(Text(`$${item.price.toFixed(2)}`).align("right")),
TableCell(Text(`$${(item.price * item.quantity).toFixed(2)}`).align("right"))
)
.padding(8, 0)
.borderBottomWidth(1)
.borderColor("#ccc")
)
),
// Totals
Column(
TableRow(
TableCell(Text("")),
TableCell(Text("")),
TableCell(Text("Subtotal:").weight("bold").align("right")),
TableCell(Text(`$${subtotal.toFixed(2)}`).align("right"))
),
TableRow(
TableCell(Text("")),
TableCell(Text("")),
TableCell(Text("Tax:").weight("bold").align("right")),
TableCell(Text(`$${tax.toFixed(2)}`).align("right"))
),
TableRow(
TableCell(Text("")),
TableCell(Text("")),
TableCell(Text("Total:").weight("bold").size(16).align("right")),
TableCell(Text(`$${total.toFixed(2)}`).weight("bold").size(16).align("right"))
)
.borderTopWidth(2)
.borderColor("black")
)
.marginTop(10)
)
.borderWidth(1)
.borderColor("black");
}
Comparison Table
function ComparisonTable(features, plans) {
return Table(
// Header row with plan names
TableRow(
TableCell(Text("Features").weight("bold")),
...plans.map(plan =>
TableCell(
Text(plan.name)
.weight("bold")
.size(16)
.color("white")
)
.padding(15)
.bg(plan.color || "#667eea")
)
),
// Feature rows
...features.map((feature, index) =>
TableRow(
TableCell(Text(feature.name).weight("500")),
...plans.map(plan => {
const hasFeature = plan.features.includes(feature.id);
return TableCell(
Text(hasFeature ? "✓" : "×")
.size(18)
.color(hasFeature ? "green" : "red")
.align("center")
);
})
)
.bg(index % 2 === 0 ? "white" : "#f9f9f9")
.padding(12, 0)
),
// Price row
TableRow(
TableCell(Text("Price").weight("bold").size(16)),
...plans.map(plan =>
TableCell(
Text(plan.price)
.weight("bold")
.size(18)
.color("green")
.align("center")
)
.bg("#f0f8f0")
)
)
)
.borderWidth(1)
.borderColor("#ddd")
.rounded(8)
.shadow("0 4px 8px rgba(0,0,0,0.1)");
}
Statistics Dashboard
function StatsTable(stats) {
return Table(
TableRow(
TableCell(Text("Metric").weight("bold").color("#666")),
TableCell(Text("Value").weight("bold").color("#666")),
TableCell(Text("Change").weight("bold").color("#666"))
)
.bg("#f8f9fa")
.borderBottomWidth(1)
.borderColor("#dee2e6"),
...stats.map(stat =>
TableRow(
TableCell(Text(stat.name).size(14)),
TableCell(Text(stat.value).size(18).weight("bold")),
TableCell(
Text(`${stat.change > 0 ? '+' : ''}${stat.change}%`)
.size(14)
.weight("500")
.color(stat.change > 0 ? "green" : stat.change < 0 ? "red" : "gray")
)
)
.padding(12, 0)
.borderBottomWidth(1)
.borderColor("#eee")
)
)
.bg("white")
.rounded(6)
.shadow("0 1px 3px rgba(0,0,0,0.1)");
}
Calendar Table
function CalendarTable(month, year, events = []) {
const daysInMonth = new Date(year, month + 1, 0).getDate();
const firstDay = new Date(year, month, 1).getDay();
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const weeks = [];
let currentWeek = [];
// Add empty cells for days before month starts
for (let i = 0; i < firstDay; i++) {
currentWeek.push(null);
}
// Add days of month
for (let day = 1; day <= daysInMonth; day++) {
currentWeek.push(day);
if (currentWeek.length === 7) {
weeks.push(currentWeek);
currentWeek = [];
}
}
// Fill last week
while (currentWeek.length < 7) {
currentWeek.push(null);
}
if (currentWeek.some(day => day !== null)) {
weeks.push(currentWeek);
}
return Table(
// Header with day names
TableRow(
...days.map(day =>
TableCell(Text(day).weight("bold").size(12))
.padding(8)
.bg("#f8f9fa")
.align("center")
)
),
// Week rows
...weeks.map(week =>
TableRow(
...week.map(day =>
TableCell(
day ? Text(day.toString()).size(14) : Text("")
)
.padding(8)
.size(40)
.bg(day ? "white" : "#f8f9fa")
.borderWidth(1)
.borderColor("#dee2e6")
.alignSelf("stretch")
)
)
)
)
.borderWidth(1)
.borderColor("#dee2e6")
.rounded(4);
}
Best Practices
Performance
- Use consistent cell sizing to optimize layout calculations
- Minimize complex content within cells
- Consider pagination for large datasets
Design
- Maintain consistent padding and spacing across cells
- Use subtle alternating row colors for readability
- Align numeric data to the right
- Left-align text data
Accessibility
- Use clear, descriptive headers
- Maintain sufficient contrast ratios
- Keep row heights consistent for easier scanning
- Consider adding visual separators for complex tables
Data Formatting
- Format numbers consistently (currency, decimals)
- Use consistent date/time formats
- Truncate long text appropriately
- Provide meaningful empty state indicators