Tabelin

A comprehensive table library with freeze headers/columns, export functionality, lazy loading, server-side processing, custom context menu, and customizable styling.

Basic Table Demo

const basicTable = new Tabelin({
    container: '#basic-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'age', title: 'Age' },
        { field: 'email', title: 'Email' }
    ],
    data: generateDemoData(20)
});
                

Freeze Header & Columns Demo Freeze

const freezeTable = new Tabelin({
    container: '#freeze-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'department', title: 'Department' },
        { field: 'position', title: 'Position' },
        { field: 'salary', title: 'Salary' },
        { field: 'startDate', title: 'Start Date' },
        { field: 'email', title: 'Email' }
    ],
    data: generateDemoData(50),
    freezeHeader: true,
    freezeColumns: 2
});
                

Pagination Demo Pagination

const paginationTable = new Tabelin({
    container: '#pagination-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'email', title: 'Email' }
    ],
    data: generateDemoData(100),
    pageSize: 10
});
                

Lazy Loading (Infinite Scroll)

Scroll down in the table below to load more data automatically without pagination. The data will continue loading as you scroll.

// Using the convenient static method
const lazyLoadingTable = Tabelin.createInfiniteTable(
    '#lazy-loading-table-container',
    [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'email', title: 'Email' },
        { field: 'department', title: 'Department' }
    ],
    generateDemoData(500),
    {
        batchSize: 15,
        loadThreshold: 100,
        freezeHeader: true
    }
);

// Equivalent to:
new Tabelin({
    container: '#lazy-loading-table-container',
    columns: [...],
    data: generateDemoData(500),
    infiniteScroll: true,
    pageSize: 15,
    loadThreshold: 100,
    freezeHeader: true
});
                

Initialize from HTML Table

Create an advanced table from an existing HTML table structure:

ID First Name Last Name Position Department
1 John Smith Manager Engineering
2 Jane Doe Developer Engineering
3 Mike Johnson Designer Marketing
4 Lisa Brown Director HR
5 David Williams Analyst Finance
// Using the convenient static method
const htmlTable = Tabelin.fromHTML('#existing-table', {
    freezeHeader: true,
    freezeColumns: 1,
    searchable: true,
    sortable: true,
    exportOptions: {
        excel: true,
        pdf: true,
        print: true
    }
});

// Equivalent to:
new Tabelin({
    container: '#html-table-container',
    useHTML: '#existing-table',
    freezeHeader: true,
    freezeColumns: 1,
    searchable: true,
    sortable: true,
    exportOptions: { excel: true, pdf: true, print: true }
});
                

Server-Side Processing New!

This example simulates a server-side data source with search, sorting, and pagination functionality.

Mock API Controls

These controls simulate server-side behavior for the demo.

// Using the convenient static method
const serverTable = Tabelin.createServerTable(
    '#server-table-container',
    [
        { field: 'id', title: 'ID', sortable: true },
        { field: 'name', title: 'Name', sortable: true },
        { field: 'email', title: 'Email' },
        { field: 'department', title: 'Department', sortable: true },
        { field: 'position', title: 'Position', sortable: true }
    ],
    'api/table-data',
    {
        pageSize: 10,
        searchable: true,
        sortable: true,
        // Custom server params function
        serverParams: function(params) {
            return {
                // Add any additional params your server needs
                token: 'demo-token',
                customParam: 'any-value'
            };
        }
    }
);
                

Server-Side Lazy Loading New!

This example simulates an infinitely scrolling server-side data source.

const serverLazyTable = new Tabelin({
    container: '#server-lazy-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'email', title: 'Email' },
        { field: 'department', title: 'Department' }
    ],
    pageSize: 20,
    serverSide: true,
    serverUrl: 'api/lazy-data',
    infiniteScroll: true,
    loadThreshold: 200,
    searchable: true
});
                

Context Menu New!

Right-click on any row in the table below to see the custom context menu.

const contextMenuTable = new Tabelin({
    container: '#context-menu-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'email', title: 'Email' },
        { field: 'department', title: 'Department' }
    ],
    data: generateDemoData(20),
    // Static context menu items
    contextMenu: [
        { 
            text: 'Edit Row',
            icon: '✏️',
            className: 'context-menu-primary',
            action: function(rowData) {
                alert(`Editing row with ID: ${rowData.id}`);
            }
        },
        { 
            text: 'Delete Row',
            icon: '🗑️',
            className: 'context-menu-danger',
            action: function(rowData) {
                alert(`Deleting row with ID: ${rowData.id}`);
            }
        },
        { divider: true },
        { 
            text: 'View Details',
            icon: '👁️',
            action: function(rowData) {
                alert(`View details for ${rowData.name}`);
            }
        },
        {
            text: 'Export Row',
            icon: '📋',
            action: function(rowData) {
                alert(`Exporting data for ${rowData.name}`);
            }
        }
    ]
});

// You can also use a function that returns contextMenu items dynamically:
const dynamicContextMenuTable = new Tabelin({
    // ...other options
    contextMenu: function(rowData) {
        return [
            // Menu items can be customized based on rowData
            { text: `Edit ${rowData.name}`, action: function() { /* do something */ } },
            // Other items...
        ];
    }
});
                

Predefined Themes New!

const themedTable = new Tabelin({
    container: '#theme-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'department', title: 'Department' },
        { field: 'position', title: 'Position' }
    ],
    data: generateDemoData(20),
    theme: 'material', // Choose from: 'default', 'dark', 'stripe', 'material', 'compact', 'colorful'
    searchable: true
});
                

Custom CSS Styling New!

You can apply your own CSS classes and styles to the table:

// Define custom CSS variables and styles in your stylesheet
.custom-theme-table {
    --header-bg: #3f51b5;
    --header-text: white;
    --row-hover: #f0f4ff;
    --border-color: #c5cae9;
    --accent-color: #3f51b5;
}

// Then apply the custom class to your table
const customStyledTable = new Tabelin({
    container: '#custom-style-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'department', title: 'Department' },
        { field: 'position', title: 'Position' }
    ],
    data: generateDemoData(15),
    cssClass: 'custom-theme-table',
    // Customize row and cell styles with callbacks
    rowClassName: function(row, index) {
        return index % 2 === 0 ? 'even-row' : 'odd-row';
    },
    cellClassName: function(value, row, column, colIndex) {
        if (column.field === 'department' && value === 'Engineering') {
            return 'highlight-cell';
        }
        return '';
    }
});
                

Custom Cell Rendering Advanced

const customRenderTable = new Tabelin({
    container: '#custom-render-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { 
            field: 'status', 
            title: 'Status',
            render: (value, row) => {
                const color = value === 'Active' ? 'green' : 'red';
                return `${value}`;
            }
        },
        { 
            field: 'progress', 
            title: 'Progress',
            render: (value, row) => {
                const percent = parseInt(value);
                return `
                    
${percent}% `; } }, { field: 'actions', title: 'Actions', render: (value, row) => { return ` `; } } ], data: generateDemoData(20).map(item => ({ ...item, status: Math.random() > 0.5 ? 'Active' : 'Inactive', progress: Math.floor(Math.random() * 100) })), searchable: true, resizableColumns: true });

Cell Editing & Collaboration New!

Double-click on any cell in the table to edit its value. Changes are tracked with version history.

const editableTable = new Tabelin({
    container: '#editable-table-container',
    columns: [
        { field: 'id', title: 'ID', editable: false },
        { field: 'name', title: 'Name' },
        { field: 'department', title: 'Department' },
        { field: 'salary', title: 'Salary' }
    ],
    data: generateDemoData(10),
    // Enable cell editing
    collaboration: true,
    collaborationMode: 'local', // 'local', 'websocket', 'polling'
    collaborationUser: { name: 'John', id: 1, color: '#4CAF50' },
    // Enable version history
    versionHistory: true,
    maxVersions: 10,
    // Add note telling users how to access version history
    footnote: 'Double-click to edit cells. Right-click and select "View Version History" to see changes.'
});
                

Data Visualizations New!

This example demonstrates integrated data visualizations with our table library.

const visualizationsTable = new Tabelin({
    container: '#visualizations-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'sales', title: 'Sales' },
        { field: 'growth', title: 'Growth %' },
        { field: 'profit', title: 'Profit' }
    ],
    data: generateSalesData(30),
    visualizations: true,
    visualizationPosition: 'summary',
    visualizationTypes: ['bar', 'line', 'pie', 'sparkline'],
    visualizationColors: ['#4CAF50', '#2196F3', '#FFC107', '#F44336', '#9C27B0'],
    // Enable AI insights to analyze data patterns
    aiInsights: true,
    theme: 'material'
});

// Generate sample sales data
function generateSalesData(count) {
    const data = [];
    const regions = ['North', 'South', 'East', 'West', 'Central'];
    const products = ['Widgets', 'Gadgets', 'Devices', 'Tools', 'Accessories'];
    
    for (let i = 1; i <= count; i++) {
        const sales = 10000 + Math.floor(Math.random() * 90000);
        const growth = -10 + Math.floor(Math.random() * 40);
        const profit = sales * (0.15 + Math.random() * 0.25); // 15-40% profit margin
        
        data.push({
            id: i,
            name: `${regions[i % regions.length]} - ${products[i % products.length]}`,
            sales: sales,
            growth: growth,
            profit: Math.round(profit)
        });
    }
    
    return data;
}
                

Conditional Formatting New!

Apply formatting rules based on cell values:

const formattingTable = new Tabelin({
    container: '#formatting-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'department', title: 'Department' },
        { field: 'salary', title: 'Salary' },
        { field: 'performance', title: 'Performance' },
        { field: 'startDate', title: 'Start Date' },
        { field: 'email', title: 'Email', width: 200 }
    ],
    data: generateDemoData(15).map(item => ({
        ...item,
        performance: ['Poor', 'Average', 'Good', 'Excellent'][Math.floor(Math.random() * 4)]
    })),
    // Enable conditional formatting
    conditionalFormatting: true,
    rules: [
        {
            field: 'salary',
            condition: value => value > 80000,
            style: { backgroundColor: '#e8f5e9', fontWeight: 'bold', color: '#2e7d32' }
        },
        {
            field: 'salary', 
            condition: value => value < 40000,
            style: { backgroundColor: '#ffebee', color: '#c62828' }
        },
        {
            field: 'performance',
            condition: value => value === 'Excellent',
            style: { backgroundColor: '#e3f2fd', fontWeight: 'bold', color: '#1565c0' }
        },
        {
            field: 'performance',
            condition: value => value === 'Poor',
            style: { backgroundColor: '#ffebee', fontStyle: 'italic', color: '#b71c1c' }
        }
    ],
    // Enable smart formatting
    smartFormatting: true,
    formatDetection: {
        numbers: true,  // Auto-format numbers (currency, percentages, etc.)
        dates: true,    // Auto-format dates
        urls: true,     // Auto-format URLs as links
        emails: true    // Auto-format emails as mailto links
    }
});
                

Business Rules & Validation New!

Apply business rules and validation to your data:

const validationTable = new Tabelin({
    container: '#validation-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'email', title: 'Email' },
        { field: 'age', title: 'Age' },
        { field: 'salary', title: 'Salary' }
    ],
    data: generateDemoData(10),
    // Make cells editable
    collaboration: true,
    // Enable validation for edits
    validateOnEdit: true,
    showValidationMessages: true,
    // Define business rules
    businessRules: [
        {
            field: 'email',
            rule: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
            message: 'Please enter a valid email address'
        },
        {
            field: 'age',
            rule: value => value >= 18 && value <= 65,
            message: 'Age must be between 18 and 65'
        },
        {
            field: 'salary',
            rule: value => value >= 30000,
            message: 'Salary must be at least $30,000'
        },
        {
            field: 'name',
            rule: value => value.length >= 2,
            message: 'Name must be at least 2 characters'
        }
    ]
});
                

AI-Powered Insights New!

Automatically analyze data patterns and display insights:

const insightsTable = new Tabelin({
    container: '#insights-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'department', title: 'Department' },
        { field: 'salary', title: 'Salary' },
        { field: 'salesTarget', title: 'Sales Target' },
        { field: 'salesActual', title: 'Sales Actual' }
    ],
    data: generateInsightData(50),
    // Enable AI insights
    aiInsights: true,
    insightsPosition: 'top', // 'top', 'bottom', 'tooltip'
    insightsThreshold: 0.7, // Confidence threshold
    // Add other options
    searchable: true,
    sortable: true,
    theme: 'material'
});

// Generate sample data with patterns for insights to detect
function generateInsightData(count) {
    const departments = ['Sales', 'Marketing', 'Engineering', 'HR', 'Finance'];
    const data = [];
    
    for (let i = 1; i <= count; i++) {
        const department = departments[Math.floor(Math.random() * departments.length)];
        const baseSalary = department === 'Sales' ? 55000 : 
                          department === 'Engineering' ? 75000 : 
                          department === 'Marketing' ? 60000 : 
                          department === 'Finance' ? 70000 : 50000;
        
        // Create salary variations within departments
        const salary = baseSalary + Math.floor(Math.random() * 30000);
        
        // Generate sales targets and actuals with meaningful relationship
        const salesTarget = department === 'Sales' ? 100000 + Math.floor(Math.random() * 50000) : 0;
        
        // Create a pattern: half of sales people exceed targets, half don't
        const performance = Math.random() > 0.5 ? 1.2 : 0.8;
        const salesActual = department === 'Sales' ? Math.floor(salesTarget * performance) : 0;
        
        const demoUser = generateDemoData(1)[0];
        data.push({
            ...demoUser,
            department,
            salary,
            salesTarget,
            salesActual
        });
    }
    
    // Add some outliers and patterns for the AI to detect
    if (data.length > 10) {
        data[3].salary = 150000; // Outlier
        data[7].salary = 25000;  // Outlier
        
        // Pattern: make everyone in marketing have better sales performance
        data.forEach(item => {
            if (item.department === 'Marketing' && item.salesTarget > 0) {
                item.salesActual = Math.floor(item.salesTarget * 1.3);
            }
        });
    }
    
    return data;
}
                

Distribution Charts & Statistics New!

View statistical distribution of your numeric data:

const distributionTable = new Tabelin({
    container: '#distribution-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'age', title: 'Age' },
        { field: 'experience', title: 'Experience (Years)' },
        { field: 'salary', title: 'Salary' },
        { field: 'performance', title: 'Performance Score' }
    ],
    data: generateDistributionData(100),
    // Enable visualizations with distribution charts
    visualizations: true,
    visualizationPosition: 'summary',
    visualizationTypes: ['distribution'],
    // Add other features
    searchable: true,
    sortable: true
});

// Generate normally distributed data
function generateDistributionData(count) {
    function normalRandom(mean, stdDev) {
        // Box-Muller transform for normal distribution
        const u1 = Math.random();
        const u2 = Math.random();
        const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
        return z0 * stdDev + mean;
    }
    
    const data = [];
    
    for (let i = 1; i <= count; i++) {
        const age = Math.round(normalRandom(35, 8));
        const experience = Math.max(0, Math.round(normalRandom(age - 22, 5))); // experience related to age
        const performance = Math.min(100, Math.max(0, Math.round(normalRandom(70, 15)))); // performance score 0-100
        
        // Salary based on experience and performance
        const baseSalary = 40000 + (experience * 2000);
        const performanceBonus = performance * 200;
        const salary = Math.round(baseSalary + performanceBonus);
        
        const demoUser = generateDemoData(1)[0];
        data.push({
            ...demoUser,
            age,
            experience,
            performance,
            salary
        });
    }
    
    return data;
}
                

Search & Sort Advanced

const searchSortTable = new Tabelin({
    container: '#search-sort-table-container',
    columns: [
        { field: 'id', title: 'ID', sortable: true },
        { field: 'name', title: 'Name', sortable: true },
        { field: 'department', title: 'Department', sortable: true },
        { field: 'salary', title: 'Salary', sortable: true },
        { field: 'startDate', title: 'Start Date', sortable: false }
    ],
    data: generateDemoData(50),
    searchable: true,
    sortable: true
});
                

Keyboard Navigation & Accessibility New!

Navigate the table using keyboard shortcuts:

Available Shortcuts:

  • Arrow Keys - Navigate between cells
  • Enter - Edit current cell (when editing is enabled)
  • Tab / Shift+Tab - Move to next/previous cell
  • Page Up / Page Down - Navigate between pages
  • Home / End - Go to first/last cell in row
  • Ctrl+Home / Ctrl+End - Go to first/last cell in table
const keyboardNavTable = new Tabelin({
    container: '#keyboard-nav-table-container',
    columns: [
        { field: 'id', title: 'ID' },
        { field: 'name', title: 'Name' },
        { field: 'email', title: 'Email' },
        { field: 'department', title: 'Department' },
        { field: 'position', title: 'Position' }
    ],
    data: generateDemoData(15),
    // Enable keyboard navigation
    keyboardNavigation: true,
    keyboardShortcuts: true,
    // Accessibility features
    accessibleHeaders: true,
    focusableRows: true,
    ariaLabels: {
        table: 'Employee Data Table',
        search: 'Search employee records',
        pagination: 'Table pagination controls',
        sortAsc: 'Sort column ascending',
        sortDesc: 'Sort column descending',
        row: index => `Employee record ${index + 1}`
    },
    // Make table cells editable
    collaboration: true
});