Sone

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