or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

content-formatting.mdevent-handling.mdindex.mdlabel-positioning.mdlabel-styling.mdmulti-label.mdplugin-integration.md
tile.json

content-formatting.mddocs/

Content Formatting

Label content control including custom formatters, display conditions, text alignment, and multi-line text handling for controlling what and how content is displayed.

Capabilities

Content Formatting

Transform and format data values for display in labels.

interface ContentOptions {
  /** Custom function to format label content
   * @default built-in formatter (converts data to string) */
  formatter?: (value: any, context: Context) => any | null;
  
  /** Control label visibility and auto-hiding
   * @default true */
  display?: Indexable<boolean | string> | Scriptable<boolean | string>;
}

interface Context {
  active: boolean;          // Whether element is hovered
  chart: Chart;            // Chart instance
  dataIndex: number;       // Index of current data point
  dataset: ChartDataset;   // Current dataset
  datasetIndex: number;    // Index of current dataset
}

Built-in Default Formatter:

The plugin includes a default formatter that handles various data types:

/**
 * Default formatter handles primitives, objects, and arrays
 * - Null/undefined returns null (no label)
 * - Objects: uses .label property, .r property, or key:value pairs
 * - Arrays and primitives: converts to string
 */
function defaultFormatter(value: any): string | null;

Usage Examples:

// Simple value formatting
datalabels: {
  formatter: (value) => '$' + value.toFixed(2)
}

// Percentage formatting
datalabels: {
  formatter: (value, context) => {
    const sum = context.dataset.data.reduce((a, b) => a + b, 0);
    return Math.round((value / sum) * 100) + '%';
  }
}

// Conditional formatting
datalabels: {
  formatter: (value, context) => {
    if (value === 0) return null; // No label for zero values
    return value > 1000 ? (value / 1000).toFixed(1) + 'K' : value;
  }
}

// Multi-line formatting
datalabels: {
  formatter: (value, context) => {
    const dataset = context.dataset;
    return [dataset.label, value]; // Array creates multiple lines
  }
}

Display Control

Control when and how labels are shown.

interface DisplayControl {
  /** Control label visibility
   * @default true */
  display?: Indexable<boolean | string> | Scriptable<boolean | string>;
}

Display Options:

  • true: Always show label (default)
  • false: Never show label
  • 'auto': Automatically hide overlapping labels
  • Function: Dynamic control based on context

Usage Examples:

// Hide labels for small values
datalabels: {
  display: function(context) {
    return context.parsed.y > 5;
  }
}

// Auto-hide overlapping labels
datalabels: {
  display: 'auto'
}

// Show only for active (hovered) elements
datalabels: {
  display: function(context) {
    return context.active;
  }
}

// Hide labels on small charts
datalabels: {
  display: function(context) {
    return context.chart.width > 300;
  }
}

Text Alignment

Control multi-line text alignment within labels.

interface TextOptions {
  /** Text alignment for multi-line labels
   * @default 'start' */
  textAlign?: Indexable<TextAlign> | Scriptable<TextAlign>;
}

type TextAlign = 'left' | 'right' | 'start' | 'center' | 'end';

Usage Examples:

// Center-aligned multi-line text
datalabels: {
  formatter: (value, context) => [
    context.dataset.label,
    '$' + value.toFixed(2)
  ],
  textAlign: 'center'
}

// Right-aligned for currency values
datalabels: {
  formatter: (value) => value.toString().split(''),
  textAlign: 'right'
}

Advanced Formatting Patterns

Complex formatting scenarios for specialized use cases.

Data Type Specific Formatting:

// Handle different chart data structures
datalabels: {
  formatter: (value, context) => {
    // Scatter plot data: {x, y}
    if (typeof value === 'object' && value.x !== undefined) {
      return `(${value.x}, ${value.y})`;
    }
    
    // Bubble chart data: {x, y, r}
    if (typeof value === 'object' && value.r !== undefined) {
      return `Size: ${value.r}`;
    }
    
    // Regular data
    return value;
  }
}

Dataset-Aware Formatting:

// Different formats per dataset
datalabels: {
  formatter: (value, context) => {
    switch (context.datasetIndex) {
      case 0: return value + ' units';
      case 1: return '$' + value;
      case 2: return value + '%';
      default: return value;
    }
  }
}

Time-Based Formatting:

// Format timestamp data
datalabels: {
  formatter: (value, context) => {
    if (value instanceof Date) {
      return value.toLocaleDateString();
    }
    // Assume timestamp in milliseconds
    return new Date(value).toLocaleDateString();
  }
}

Statistical Formatting:

// Show rank, value, and percentage
datalabels: {
  formatter: (value, context) => {
    const data = context.dataset.data;
    const sorted = [...data].sort((a, b) => b - a);
    const rank = sorted.indexOf(value) + 1;
    const total = data.reduce((sum, val) => sum + val, 0);
    const percentage = ((value / total) * 100).toFixed(1);
    
    return [
      `#${rank}`,
      value,
      `${percentage}%`
    ];
  }
}

Conditional Multi-Format:

// Different formatting based on value range
datalabels: {
  formatter: (value, context) => {
    if (value === 0) return null;
    
    if (value < 1) {
      return (value * 100).toFixed(1) + '%';
    } else if (value < 1000) {
      return value.toString();
    } else if (value < 1000000) {
      return (value / 1000).toFixed(1) + 'K';
    } else {
      return (value / 1000000).toFixed(1) + 'M';
    }
  }
}

Responsive Formatting:

// Adjust format based on available space
datalabels: {
  formatter: (value, context) => {
    const chartWidth = context.chart.width;
    
    if (chartWidth < 400) {
      // Short format for small screens
      return value > 1000 ? Math.round(value / 1000) + 'K' : value;
    } else {
      // Full format for larger screens
      return new Intl.NumberFormat().format(value);
    }
  }
}