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

event-handling.mddocs/

Event Handling

Interactive label functionality with event listeners for click, hover, enter, and leave events enabling dynamic label behavior and user interactions.

Capabilities

Event Listeners

Attach interactive behaviors to data labels with context-aware event handling.

interface EventOptions {
  /** Event listener configuration */
  listeners?: {
    /** Called when a label is clicked */
    click?: Listener;
    /** Called when mouse enters a label */
    enter?: Listener;
    /** Called when mouse leaves a label */
    leave?: Listener;
  };
}

type Listener = (context: Context, event: ChartEvent) => boolean | void;

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

Return Values:

  • true: Update the label and re-render the chart
  • false or undefined: No action needed

Usage Examples:

// Basic click handler
datalabels: {
  listeners: {
    click: function(context, event) {
      console.log('Clicked:', context.dataset.data[context.dataIndex]);
      return false; // No chart update needed
    }
  }
}

// Hover effects
datalabels: {
  backgroundColor: 'lightblue',
  listeners: {
    enter: function(context, event) {
      // Change to hover color
      context.backgroundColor = 'darkblue';
      return true; // Trigger chart update
    },
    leave: function(context, event) {
      // Reset to default color
      context.backgroundColor = 'lightblue';
      return true; // Trigger chart update
    }
  }
}

Click Events

Handle user clicks on data labels for interactive functionality.

Usage Examples:

// Data point selection
datalabels: {
  listeners: {
    click: function(context, event) {
      const chart = context.chart;
      const dataIndex = context.dataIndex;
      const datasetIndex = context.datasetIndex;
      
      // Toggle data point visibility
      const meta = chart.getDatasetMeta(datasetIndex);
      meta.data[dataIndex].hidden = !meta.data[dataIndex].hidden;
      chart.update();
      
      return false;
    }
  }
}

// External callbacks
datalabels: {
  listeners: {
    click: function(context, event) {
      // Call external function with data
      window.handleDataPointClick({
        value: context.dataset.data[context.dataIndex],
        label: context.dataset.label,
        dataIndex: context.dataIndex,
        datasetIndex: context.datasetIndex
      });
      return false;
    }
  }
}

// Drill-down functionality
datalabels: {
  listeners: {
    click: function(context, event) {
      const value = context.dataset.data[context.dataIndex];
      const label = context.chart.data.labels[context.dataIndex];
      
      // Navigate to detail view
      window.location.href = `/details/${label}/${value}`;
      return false;
    }
  }
}

Hover Events

Create interactive hover effects that respond to mouse movements.

Usage Examples:

// Highlight on hover
datalabels: {
  color: 'black',
  font: { weight: 'normal' },
  listeners: {
    enter: function(context, event) {
      // Highlight style
      context.color = 'red';
      context.font.weight = 'bold';
      return true;
    },
    leave: function(context, event) {
      // Reset style
      context.color = 'black';
      context.font.weight = 'normal';
      return true;
    }
  }
}

// Dynamic content on hover
datalabels: {
  formatter: function(value, context) {
    if (context.active) {
      // Show detailed info when hovered
      const percentage = ((value / context.dataset.data.reduce((a, b) => a + b)) * 100).toFixed(1);
      return `${value} (${percentage}%)`;
    }
    return value;
  },
  listeners: {
    enter: function(context, event) {
      return true; // Trigger re-render to show detailed format
    },
    leave: function(context, event) {
      return true; // Trigger re-render to show simple format
    }
  }
}

// Tooltip-like behavior
datalabels: {
  display: false, // Hidden by default
  listeners: {
    enter: function(context, event) {
      context.display = true;
      return true;
    },
    leave: function(context, event) {
      context.display = false;
      return true;
    }
  }
}

Advanced Event Patterns

Complex interactive behaviors using event combinations.

Multi-State Interactions:

// Track selection state
let selectedPoints = new Set();

datalabels: {
  backgroundColor: function(context) {
    const key = `${context.datasetIndex}-${context.dataIndex}`;
    return selectedPoints.has(key) ? 'orange' : 'lightblue';
  },
  listeners: {
    click: function(context, event) {
      const key = `${context.datasetIndex}-${context.dataIndex}`;
      
      if (selectedPoints.has(key)) {
        selectedPoints.delete(key);
      } else {
        selectedPoints.add(key);
      }
      
      return true; // Update to reflect selection state
    },
    enter: function(context, event) {
      const key = `${context.datasetIndex}-${context.dataIndex}`;
      if (!selectedPoints.has(key)) {
        context.backgroundColor = 'yellow'; // Hover state
        return true;
      }
      return false;
    },
    leave: function(context, event) {
      const key = `${context.datasetIndex}-${context.dataIndex}`;
      if (!selectedPoints.has(key)) {
        context.backgroundColor = 'lightblue'; // Reset
        return true;
      }
      return false;
    }
  }
}

Conditional Event Handling:

// Different behaviors based on data
datalabels: {
  listeners: {
    click: function(context, event) {
      const value = context.dataset.data[context.dataIndex];
      
      if (value > 100) {
        // High value action
        showDetailModal(context);
      } else if (value > 50) {
        // Medium value action
        highlightRelatedData(context);
      } else {
        // Low value action
        showWarning('Value is quite low');
      }
      
      return false;
    }
  }
}

Event Delegation:

// Centralized event handling
function createEventHandler(action) {
  return function(context, event) {
    // Log all interactions
    console.log(`${action} on`, {
      dataset: context.dataset.label,
      value: context.dataset.data[context.dataIndex],
      index: context.dataIndex
    });
    
    // Dispatch to appropriate handler
    switch (action) {
      case 'click':
        handleClick(context, event);
        break;
      case 'enter':
        handleHover(context, event, true);
        break;
      case 'leave':
        handleHover(context, event, false);
        break;
    }
    
    return true;
  };
}

datalabels: {
  listeners: {
    click: createEventHandler('click'),
    enter: createEventHandler('enter'),
    leave: createEventHandler('leave')
  }
}

Cross-Chart Interactions:

// Sync multiple charts
datalabels: {
  listeners: {
    enter: function(context, event) {
      // Highlight corresponding data in other charts
      const otherCharts = getAllCharts().filter(c => c !== context.chart);
      otherCharts.forEach(chart => {
        highlightDataPoint(chart, context.dataIndex);
      });
      return false;
    },
    leave: function(context, event) {
      // Clear highlights in other charts
      const otherCharts = getAllCharts().filter(c => c !== context.chart);
      otherCharts.forEach(chart => {
        clearHighlights(chart);
      });
      return false;
    }
  }
}

Performance Considerations

Optimize event handling for smooth interactions.

Debounced Events:

// Debounce hover events for performance
let hoverTimeout;

datalabels: {
  listeners: {
    enter: function(context, event) {
      clearTimeout(hoverTimeout);
      hoverTimeout = setTimeout(() => {
        // Delayed hover action
        performExpensiveHoverAction(context);
      }, 150);
      return false;
    },
    leave: function(context, event) {
      clearTimeout(hoverTimeout);
      return false;
    }
  }
}

Event Throttling:

// Throttle frequent events
let lastEventTime = 0;
const EVENT_THROTTLE = 100; // ms

datalabels: {
  listeners: {
    enter: function(context, event) {
      const now = Date.now();
      if (now - lastEventTime < EVENT_THROTTLE) {
        return false;
      }
      lastEventTime = now;
      
      // Handle event
      return handleHoverEvent(context, event);
    }
  }
}