CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jspdf-autotable

Generate PDF tables with JavaScript (jsPDF plugin)

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

hook-system.mddocs/

Hook System

Event-driven customization system for intercepting and modifying table rendering at various stages, enabling advanced customization and dynamic content generation.

Capabilities

Hook Data Classes

Base classes providing context information for hook functions.

HookData Class

Base hook data class providing table and document context.

class HookData {
  /** Reference to the table being rendered */
  table: Table;
  /** Current page number */
  pageNumber: number;
  /** Table settings */
  settings: Settings;
  /** jsPDF document instance */
  doc: jsPDFDocument;
  /** Current cursor position */
  cursor: Pos | null;

  constructor(doc: DocHandler, table: Table, cursor: Pos | null);
}

CellHookData Class

Extended hook data class for cell-specific events.

class CellHookData extends HookData {
  /** Reference to the current cell */
  cell: Cell;
  /** Reference to the current row */
  row: Row;
  /** Reference to the current column */
  column: Column;
  /** Section type: 'head', 'body', or 'foot' */
  section: "head" | "body" | "foot";

  constructor(
    doc: DocHandler,
    table: Table,
    cell: Cell,
    row: Row,
    column: Column,
    cursor: Pos | null
  );
}

Hook Function Types

Function signatures for different hook events.

/** Page-level hook function */
type PageHook = (data: HookData) => void | boolean;

/** Cell-level hook function */
type CellHook = (data: CellHookData) => void | boolean;

Hook Events

Available hook events for customizing table rendering behavior.

didParseCell Hook

Called after the plugin has finished parsing cell content. Use this to override content or styles for specific cells.

/** Called when the plugin finished parsing cell content */
didParseCell?: CellHook;

Usage Example:

autoTable(doc, {
  head: [['Name', 'Amount', 'Status']],
  body: [
    ['John Doe', '1500', 'active'],
    ['Jane Smith', '2300', 'inactive'],
  ],
  didParseCell: (data) => {
    const { cell, column } = data;
    
    // Format currency
    if (column.dataKey === 1) { // Amount column
      const amount = parseFloat(cell.text[0]);
      cell.text = [`$${amount.toLocaleString()}`];
    }
    
    // Style status column
    if (column.dataKey === 2) { // Status column
      if (cell.text[0] === 'active') {
        cell.styles.textColor = [0, 128, 0];
        cell.text = ['✓ Active'];
      } else {
        cell.styles.textColor = [128, 0, 0];
        cell.text = ['✗ Inactive'];
      }
    }
  },
});

willDrawCell Hook

Called before a cell is drawn. Use this to modify styling or positioning before rendering.

/** Called before a cell or row is drawn */
willDrawCell?: CellHook;

Usage Example:

autoTable(doc, {
  head: [['Product', 'Stock', 'Price']],
  body: [
    ['Laptop', '5', '999'],
    ['Phone', '0', '599'],
    ['Tablet', '12', '399'],
  ],
  willDrawCell: (data) => {
    const { cell, column, doc } = data;
    
    // Highlight low stock items
    if (column.dataKey === 1) { // Stock column
      const stock = parseInt(cell.text[0]);
      if (stock === 0) {
        cell.styles.fillColor = [255, 200, 200]; // Light red
        cell.styles.fontStyle = 'bold';
      } else if (stock < 10) {
        cell.styles.fillColor = [255, 255, 200]; // Light yellow
      }
    }
    
    // Apply custom jsPDF styling
    if (column.dataKey === 2) { // Price column
      doc.setTextColor(0, 100, 0); // Dark green for prices
    }
  },
});

didDrawCell Hook

Called after a cell has been drawn. Use this to add additional content like images, shapes, or custom text.

/** Called after a cell has been added to the page */
didDrawCell?: CellHook;

Usage Example:

autoTable(doc, {
  head: [['Name', 'Rating', 'Notes']],
  body: [
    ['Product A', '5', 'Excellent quality'],
    ['Product B', '3', 'Average'],
    ['Product C', '4', 'Good value'],
  ],
  didDrawCell: (data) => {
    const { cell, column, doc } = data;
    
    // Draw star ratings
    if (column.dataKey === 1) { // Rating column
      const rating = parseInt(cell.text[0]);
      const startX = cell.x + 5;
      const centerY = cell.y + cell.height / 2;
      
      // Draw stars
      for (let i = 0; i < 5; i++) {
        const x = startX + (i * 10);
        if (i < rating) {
          doc.setFillColor(255, 215, 0); // Gold
          doc.circle(x, centerY, 3, 'F');
        } else {
          doc.setDrawColor(200, 200, 200);
          doc.circle(x, centerY, 3, 'S');
        }
      }
    }
    
    // Add custom borders
    if (column.dataKey === 2) { // Notes column
      doc.setDrawColor(100, 100, 100);
      doc.setLineWidth(0.5);
      doc.line(cell.x, cell.y, cell.x + cell.width, cell.y);
    }
  },
});

willDrawPage Hook

Called before starting to draw content on a page. Use this to add headers, watermarks, or page setup.

/** Called before starting to draw on a page */
willDrawPage?: PageHook;

Usage Example:

autoTable(doc, {
  head: [['Item', 'Quantity', 'Price']],
  body: largeDataSet,
  willDrawPage: (data) => {
    const { doc, table, pageNumber } = data;
    
    // Add header to each page
    doc.setFontSize(16);
    doc.setFont('helvetica', 'bold');
    doc.text('Sales Report', 20, 20);
    
    // Add page number
    doc.setFontSize(10);
    doc.setFont('helvetica', 'normal');
    doc.text(`Page ${pageNumber}`, 200, 20);
    
    // Add watermark
    doc.setTextColor(200, 200, 200);
    doc.setFontSize(48);
    doc.text('DRAFT', 105, 150, { 
      angle: 45, 
      align: 'center' 
    });
    
    // Reset text color for table content
    doc.setTextColor(0, 0, 0);
    doc.setFontSize(10);
  },
});

didDrawPage Hook

Called after the plugin has finished drawing everything on a page. Use this to add footers, signatures, or final page elements.

/** Called after the plugin has finished drawing everything on a page */
didDrawPage?: PageHook;

Usage Example:

autoTable(doc, {
  head: [['Description', 'Amount']],
  body: invoiceItems,
  didDrawPage: (data) => {
    const { doc, table, pageNumber, cursor } = data;
    
    // Add footer
    const pageHeight = doc.internal.pageSize.height;
    const pageWidth = doc.internal.pageSize.width;
    
    doc.setFontSize(8);
    doc.setTextColor(100, 100, 100);
    
    // Company information
    doc.text('MyCompany Inc.', 20, pageHeight - 20);
    doc.text('123 Business St, City, State 12345', 20, pageHeight - 15);
    doc.text('Phone: (555) 123-4567', 20, pageHeight - 10);
    
    // Page number and date
    doc.text(`Page ${pageNumber}`, pageWidth - 50, pageHeight - 20);
    doc.text(new Date().toLocaleDateString(), pageWidth - 50, pageHeight - 15);
    
    // Add total on last page
    if (cursor && cursor.y > pageHeight - 100) {
      const totalY = cursor.y + 10;
      doc.setFontSize(12);
      doc.setFont('helvetica', 'bold');
      doc.text('Total: $1,234.56', pageWidth - 80, totalY);
    }
  },
});

Hook Properties Interface

Collection of all hook functions for a table.

interface HookProps {
  didParseCell: CellHook[];
  willDrawCell: CellHook[];
  didDrawCell: CellHook[];
  willDrawPage: PageHook[];
  didDrawPage: PageHook[];
}

Advanced Hook Patterns

Conditional Content Modification

const didParseCell = (data: CellHookData) => {
  const { cell, column, row, section } = data;
  
  // Only modify body cells
  if (section !== 'body') return;
  
  // Format dates in specific column
  if (column.dataKey === 'date') {
    const date = new Date(cell.text[0]);
    cell.text = [date.toLocaleDateString()];
  }
  
  // Add row numbers
  if (column.index === 0) {
    cell.text = [`${row.index + 1}. ${cell.text[0]}`];
  }
};

Dynamic Styling Based on Data

const willDrawCell = (data: CellHookData) => {
  const { cell, row, table } = data;
  
  // Alternate row colors differently for each section
  switch (data.section) {
    case 'head':
      cell.styles.fillColor = [52, 73, 94];
      cell.styles.textColor = [255, 255, 255];
      break;
      
    case 'body':
      if (row.index % 2 === 0) {
        cell.styles.fillColor = [245, 245, 245];
      }
      break;
      
    case 'foot':
      cell.styles.fillColor = [52, 152, 219];
      cell.styles.textColor = [255, 255, 255];
      cell.styles.fontStyle = 'bold';
      break;
  }
};

Multi-Page Headers and Footers

let pageCount = 0;

const willDrawPage = (data: HookData) => {
  pageCount++;
  const { doc } = data;
  
  // Different header for first page
  if (pageCount === 1) {
    doc.setFontSize(20);
    doc.text('Annual Report 2024', 20, 30);
    doc.setFontSize(12);
    doc.text('Confidential Document', 20, 40);
  } else {
    doc.setFontSize(12);
    doc.text('Annual Report 2024 (Continued)', 20, 20);
  }
};

const didDrawPage = (data: HookData) => {
  const { doc, pageNumber } = data;
  const pageHeight = doc.internal.pageSize.height;
  
  // Page footer with different content for last page
  if (pageNumber === pageCount) {
    doc.text('End of Report', 20, pageHeight - 10);
  } else {
    doc.text('Continued on next page...', 20, pageHeight - 10);
  }
};

Hook Return Values

Hooks can return false to prevent default behavior:

const willDrawCell = (data: CellHookData) => {
  const { cell, column } = data;
  
  // Skip drawing empty cells
  if (!cell.text[0] || cell.text[0].trim() === '') {
    return false; // Prevents cell from being drawn
  }
  
  // Skip drawing specific columns
  if (column.dataKey === 'internal_id') {
    return false;
  }
};

Position and Cursor Information

type Pos = { x: number; y: number };

Hook functions receive cursor position information for precise element placement:

const didDrawCell = (data: CellHookData) => {
  const { cell, doc, cursor } = data;
  
  console.log(`Cell position: (${cell.x}, ${cell.y})`);
  console.log(`Cell size: ${cell.width} x ${cell.height}`);
  console.log(`Cursor position: (${cursor?.x}, ${cursor?.y})`);
  
  // Use position for custom drawing
  doc.circle(cell.x + cell.width - 5, cell.y + 5, 2, 'F');
};

docs

configuration-styling.md

core-functionality.md

data-models.md

hook-system.md

index.md

plugin-integration.md

tile.json