Tables
DataTable-like functionality for interactive data grids with sorting, filtering, pagination, and more.
Creating Tables
Transform a table element into an interactive data grid.
// Create a table instance
const table = Domma.tables.create('#my-table', {
data: [
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'Admin' },
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'User' },
// ...more rows
],
columns: [
{ key: 'id', title: 'ID', sortable: true },
{ key: 'name', title: 'Name', sortable: true },
{ key: 'email', title: 'Email', sortable: true, editable: true },
{ key: 'role', title: 'Role', filterable: true },
{
key: 'actions',
title: '',
render: (val, row) => `Edit`
}
],
pagination: true,
pageSize: 10,
selectable: true
});
// HTML
Column Options
| Option | Type | Description |
|---|---|---|
key |
string | Property name in data object |
title |
string | Column header text |
sortable |
boolean | Enable sorting on this column |
filterable |
boolean | Enable filtering on this column |
editable |
boolean | Enable inline editing |
render |
function | Custom cell renderer (val, row) => html |
Custom Render Examples
Use the render function to transform cell values - perfect for date formatting, badges, or custom HTML.
// Format dates using Domma's date module
{
key: 'joined',
title: 'Joined',
sortable: true,
render: (val) => D(val).format('DD/MM/YYYY') // 15/01/2024
}
// Status badge with conditional styling
{
key: 'status',
title: 'Status',
render: (val) => `${val}`
}
// Action buttons with row data
{
key: 'actions',
title: '',
render: (val, row) => `Edit ${row.name}`
}
Tip: Use width: '100px' or width: '20%' to set column widths.
Table Options
| Option | Type | Default | Description |
|---|---|---|---|
data |
array | [] | Array of row objects |
columns |
array | [] | Column configuration |
pagination |
boolean | false | Enable pagination |
pageSize |
number | 10 | Rows per page |
selectable |
boolean | false | Enable row selection |
selectionMode |
string | 'multiple' | 'single' or 'multiple' |
columnToggle |
boolean | false | Show column visibility dropdown |
Interactive Demo
This demo includes the Columns dropdown (to show/hide columns) and the Export Panel (Copy/Download toggle). Note the Joined column uses a custom render function to format dates as DD/MM/YYYY.
<div id="demo-table"></div>
<div class="flex gap-2">
<select id="role-filter" class="form-select" style="width: 150px;">
<option value="">All Roles</option>
<option value="Admin">Admin</option>
<option value="Manager">Manager</option>
<option value="Developer">Developer</option>
<option value="Designer">Designer</option>
</select>
<button type="button" class="btn btn-outline btn-sm" id="clear-filters">Clear Filters</button>
</div>
<div class="flex gap-2">
<button type="button" class="btn btn-outline btn-sm" id="select-all">Select All</button>
<button type="button" class="btn btn-outline btn-sm" id="deselect-all">Deselect All</button>
<button type="button" class="btn btn-danger btn-sm" id="delete-selected" disabled="">Delete Selected</button>
</div>
<div id="selected-info" class="text-sm text-muted mt-3">No rows selected</div>/**
* Interactive Demo - from public/showcase/tables.
*/
export function mount(root) {
const sampleData = [
{
id: 1,
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Admin',
status: 'Active',
joined: '2024-01-15'
},
{id: 2, name: 'Bob Smith', email: 'bob@example.com', role: 'Developer', status: 'Active', joined: '2024-02-20'},
{
id: 3,
name: 'Carol White',
email: 'carol@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-03-10'
},
{
id: 4,
name: 'David Brown',
email: 'david@example.com',
role: 'Manager',
status: 'Inactive',
joined: '2024-01-05'
},
{
id: 5,
name: 'Eva Martinez',
email: 'eva@example.com',
role: 'Developer',
status: 'Active',
joined: '2024-04-01'
},
{
id: 6,
name: 'Frank Wilson',
email: 'frank@example.com',
role: 'Admin',
status: 'Active',
joined: '2024-02-14'
},
{
id: 7,
name: 'Grace Lee',
email: 'grace@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-05-22'
},
{
id: 8,
name: 'Henry Davis',
email: 'henry@example.com',
role: 'Developer',
status: 'Inactive',
joined: '2024-03-30'
},
{id: 9, name: 'Ivy Chen', email: 'ivy@example.com', role: 'Manager', status: 'Active', joined: '2024-06-15'},
{
id: 10,
name: 'Jack Taylor',
email: 'jack@example.com',
role: 'Developer',
status: 'Active',
joined: '2024-04-18'
},
{
id: 11,
name: 'Kate Anderson',
email: 'kate@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-07-01'
},
{id: 12, name: 'Leo Thomas', email: 'leo@example.com', role: 'Admin', status: 'Inactive', joined: '2024-01-28'},
{
id: 13,
name: 'Mia Jackson',
email: 'mia@example.com',
role: 'Developer',
status: 'Active',
joined: '2024-08-10'
},
{
id: 14,
name: 'Noah Garcia',
email: 'noah@example.com',
role: 'Manager',
status: 'Active',
joined: '2024-05-05'
},
{
id: 15,
name: 'Olivia Moore',
email: 'olivia@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-09-20'
}
];
// Create table with export panel
const table = Domma.tables.create('#demo-table', {
data: sampleData,
columns: [
{key: 'id', title: 'ID', sortable: true, width: '60px'},
{key: 'name', title: 'Name', sortable: true},
{key: 'email', title: 'Email', sortable: true},
{key: 'role', title: 'Role', sortable: true, filterable: true},
{
key: 'status',
title: 'Status',
render: (val) => `<span class="badge ${val === 'Active' ? 'badge-success' : 'badge-secondary'}">${val}</span>`
},
{
key: 'joined',
title: 'Joined',
sortable: true,
render: (val) => D(val).format('DD/MM/YYYY')
}
],
pagination: true,
pageSize: 10,
selectable: true,
selectionMode: 'multiple',
// Column visibility toggle
columnToggle: true,
// Regex search toggle
regexSearch: true,
// Export panel options
exportPanel: true,
exportOptions: ['copy', 'csv', 'excel', 'json'],
onExport: (info) => {
console.log(`Exported ${info.rowCount} rows as ${info.format}`);
}
});
function updateSelectedInfo() {
const selected = table.getSelected();
if (selected.length === 0) {
$(root).find('#selected-info').text('No rows selected');
$(root).find('#delete-selected').prop('disabled', true);
} else {
const names = selected.slice(0, 3).map(r => r.name).join(', ');
const extra = selected.length > 3 ? ` and ${selected.length - 3} more` : '';
$(root).find('#selected-info').text(`${selected.length} row(s) selected: ${names}${extra}`);
$(root).find('#delete-selected').prop('disabled', false);
}
}
// Table events
table.on('select', () => {
updateSelectedInfo();
});
// Role filter
$(root).find('#role-filter').on('change', function () {
const role = $(this).val();
if (role) {
table.filter('role', role);
} else {
table.clearFilters();
}
});
$(root).find('#clear-filters').on('click', function () {
$(root).find('#role-filter').val('');
table.clearFilters();
});
// Selection buttons
$(root).find('#select-all').on('click', () => table.selectAll());
$(root).find('#deselect-all').on('click', () => table.deselectAll());
$(root).find('#delete-selected').on('click', function () {
const selected = table.getSelected();
if (selected.length && confirm(`Delete ${selected.length} row(s)?`)) {
// Remove by row key (id)
selected.forEach(row => table.removeRow(row.id));
}
});
const names = ['Alex', 'Blake', 'Casey', 'Dana', 'Ellis', 'Finn'];
// ============================================
// Stripe Styling Demo
// ============================================
// Sample data for stripe demo
const stripeData = sampleData.slice(0, 6);
}Sorting
Sort data by one or multiple columns.
// Single column sort
table.sort('name', 'asc');
table.sort('age', 'desc');
// Multi-column sort
table.sortMultiple([
{ column: 'department', direction: 'asc' },
{ column: 'name', direction: 'asc' }
]);
// Clear sorting
table.clearSort();
// Listen for sort events
table.on('sort', (column, direction) => {
console.log(`Sorted by ${column} ${direction}`);
});
Click column headers in the demo table above to sort. Click again to reverse order.
<p class="text-sm text-muted mb-3">Click column headers in the demo table above to sort. Click again to
reverse order.</p>
<div class="flex gap-2">
<button type="button" class="btn btn-outline btn-sm" id="sort-name">Sort by Name</button>
<button type="button" class="btn btn-outline btn-sm" id="sort-role">Sort by Role</button>
<button type="button" class="btn btn-outline btn-sm" id="sort-date">Sort by Date</button>
<button type="button" class="btn btn-outline btn-sm" id="clear-sort">Clear Sort</button>
</div>/**
* Sorting - from public/showcase/tables.
*/
export function mount(root) {
// Sorting buttons
$(root).find('#sort-name').on('click', () => table.sort('name', 'asc'));
$(root).find('#sort-role').on('click', () => table.sort('role', 'asc'));
$(root).find('#sort-date').on('click', () => table.sort('joined', 'desc'));
$(root).find('#clear-sort').on('click', () => table.clearSort());
}Filtering
Filter data with text search or column-specific filters.
// Global text search
table.search('alice');
// Column filter
table.filter('role', 'Admin');
table.filter('status', 'active', 'equals');
// Filter operators
table.filter('age', 25, 'gt'); // Greater than
table.filter('age', 30, 'lt'); // Less than
table.filter('age', 25, 'gte'); // Greater or equal
table.filter('name', 'al', 'contains');
// Custom filter function
table.filterBy((row) => {
return row.age >= 25 && row.status === 'active';
});
// Clear filters
table.clearFilters();
// Listen for filter events
table.on('filter', (filters) => {
console.log('Active filters:', filters);
});
Pagination
Navigate through large datasets with pagination controls.
// Create table with pagination
const table = Domma.tables.create('#table', {
data: largeDataset,
pagination: true,
pageSize: 25
});
// Navigate pages
table.page(1); // Go to page 1 (0-indexed)
table.nextPage();
table.prevPage();
table.firstPage();
table.lastPage();
// Change page size
table.pageSize(50);
// Get pagination info
const info = table.pageInfo();
// {
// currentPage: 0,
// pageSize: 25,
// totalPages: 10,
// totalRows: 250,
// startRow: 1,
// endRow: 25
// }
// Listen for page events
table.on('page', (page, pageSize) => {
console.log(`Page ${page + 1}`);
});
<div class="flex gap-2 items-center">
<span class="text-sm">Page Size:</span>
<select id="page-size" class="form-select" style="width: 80px;">
<option value="5">5</option>
<option value="10" selected="">10</option>
<option value="25">25</option>
<option value="50">50</option>
</select>
<button type="button" class="btn btn-outline btn-sm" id="first-page">First</button>
<button type="button" class="btn btn-outline btn-sm" id="prev-page">Prev</button>
<button type="button" class="btn btn-outline btn-sm" id="next-page">Next</button>
<button type="button" class="btn btn-outline btn-sm" id="last-page">Last</button>
</div>Row Selection
Select single or multiple rows for bulk operations.
// Enable selection
const table = Domma.tables.create('#table', {
data: data,
selectable: true,
selectionMode: 'multiple' // or 'single'
});
// Select rows
table.select(0); // Select first row
table.select([0, 1, 2]); // Select multiple rows
table.selectAll();
// Deselect
table.deselect(0);
table.deselectAll();
// Toggle selection
table.toggleSelect(0);
// Get selected rows
const selected = table.getSelected();
// Returns array of selected row data
const selectedIndices = table.getSelectedIndices();
// Returns array of selected row indices
// Listen for selection events
table.on('select', (selectedRows, selectedIndices) => {
console.log(`${selectedRows.length} rows selected`);
});
Use checkboxes in the demo table to select rows. Selected rows are highlighted.
<p class="text-sm text-muted mb-3">Use checkboxes in the demo table to select rows. Selected rows are
highlighted.</p>
<div id="selected-info" class="text-sm">No rows selected</div>Data Management
Add, update, and remove data dynamically.
// Set all data
table.setData(newDataArray);
// Get all data (filtered/sorted state)
const currentData = table.getData();
// Get original data
const originalData = table.getOriginalData();
// Add rows
table.addRow({ id: 100, name: 'New User' });
[{ id: 101, name: 'Maya' }, { id: 102, name: 'Alex' }]
.forEach(row => table.addRow(row));
// Update row
table.updateRow(0, { name: 'Updated Name' });
table.updateRow(0, newRowData, true); // Replace entire row
// Remove rows
table.removeRow(0);
[2, 1, 0].forEach(index => table.removeRow(index));
// Clear all data
table.setData([]);
// Refresh table (re-render)
table.refresh();
<div class="flex gap-2">
<button type="button" class="btn btn-success btn-sm" id="add-row">Add Random Row</button>
<button type="button" class="btn btn-warning btn-sm" id="update-first">Update First Row</button>
<button type="button" class="btn btn-danger btn-sm" id="remove-last">Remove Last Row</button>
</div>/**
* Data Management - from public/showcase/tables.
*/
export function mount(root) {
// Data manipulation
let nextId = 100;
const roles = ['Admin', 'Manager', 'Developer', 'Designer'];
const names = ['Alex', 'Blake', 'Casey', 'Dana', 'Ellis', 'Finn'];
$(root).find('#add-row').on('click', function () {
const name = _.sample(names) + ' ' + _.sample(names);
table.addRow({
id: nextId++,
name: name,
email: name.toLowerCase().replace(' ', '.') + '@example.com',
role: _.sample(roles),
status: _.random(0, 9) > 2 ? 'Active' : 'Inactive',
joined: D().format('YYYY-MM-DD')
});
});
$(root).find('#update-first').on('click', function () {
table.updateRow(0, {name: 'Updated Name ' + _.random(0, 99)});
});
$(root).find('#remove-last').on('click', function () {
const data = table.getData();
if (data.length > 0) {
table.removeRow(data.length - 1);
}
});
}Export Panel
Enable a built-in export toolbar with Copy/Download toggle and format buttons.
// Enable export panel
const table = Domma.tables.create('#my-table', {
data: myData,
columns: [...],
exportPanel: true, // Enable the export toolbar
exportOptions: ['copy', 'csv', 'excel', 'json'], // Choose which buttons to show
onExport: (info) => {
console.log(`Exported ${info.rowCount} rows as ${info.format}`);
}
});
// The panel shows:
// [ Copy (•)──Download ] | 📋 Text 📄 CSV 📊 Excel { } JSON
//
// Toggle LEFT = Copy mode (copies to clipboard)
// Toggle RIGHT = Download mode (downloads file)
Export Panel Options
| Option | Type | Default | Description |
|---|---|---|---|
exportPanel |
boolean | false | Show the export toolbar |
exportOptions |
array | ['copy', 'csv', 'excel', 'json'] | Which export buttons to display |
onExport |
function | null | Callback when data is exported |
Column Visibility
Enable a dropdown to show/hide columns dynamically. Users can toggle column visibility without reloading the table.
// Enable column toggle dropdown
const table = Domma.tables.create('#my-table', {
data: myData,
columns: [
{ key: 'id', title: 'ID' },
{ key: 'name', title: 'Name' },
{ key: 'email', title: 'Email', visible: false }, // Hidden by default
{ key: 'role', title: 'Role' }
],
columnToggle: true // Enable the Columns dropdown
});
// Programmatic control
table.showColumn('email'); // Show a hidden column
table.hideColumn('role'); // Hide a visible column
table.toggleColumn('name'); // Toggle visibility
// Get column info
table.getVisibleColumns(); // Array of visible column configs
table.getHiddenColumns(); // Array of hidden column configs
table.getColumns(); // All columns
Column Visibility Options
| Option | Type | Default | Description |
|---|---|---|---|
columnToggle |
boolean | false | Show the Columns dropdown in the toolbar |
regexSearch |
boolean | false | Show the regex toggle button next to search input |
visible (column) |
boolean | true | Initial visibility of a column |
Try the Columns dropdown in the Interactive Demo above to show/hide columns.
<p class="text-sm text-muted mb-3">Try the <strong>Columns</strong> dropdown in the Interactive Demo
above to show/hide columns.</p>Stripe Styling Configuration
Customise row stripe and hover colours using named variants or custom hex/RGB values.
// Named variants (uses CSS variables)
const table1 = Domma.tables.create('#table', {
data: data,
striped: true,
evenRowColor: 'lighter', // var(--dm-gray-100)
oddRowColor: 'light', // var(--dm-gray-200)
hoverColor: 'medium' // var(--dm-gray-300)
});
// Custom hex colors
const table2 = Domma.tables.create('#table', {
data: data,
striped: true,
evenRowColor: '#ffffff',
oddRowColor: '#f9f9f9',
hoverColor: '#e6f2ff'
});
// Semantic tints
const table3 = Domma.tables.create('#table', {
data: data,
striped: true,
evenRowColor: 'none',
oddRowColor: 'primary-tint',
hoverColor: 'light'
});
// Named variants: 'none', 'lighter', 'light', 'medium', 'dark',
// 'primary-tint', 'success-tint', 'warning-tint',
// 'danger-tint', 'info-tint'
Stripe Colour Options
| Option | Type | Default | Description |
|---|---|---|---|
evenRowColor |
string | null (transparent) |
Colour for even rows (0, 2, 4...). Named variant or hex/rgb value. |
oddRowColor |
string | null ('#f9f9f9') |
Colour for odd rows (1, 3, 5...). Named variant or hex/rgb value. |
hoverColor |
string | null ('#f0f0f0') |
Colour for row hover state. Named variant or hex/rgb value. |
Interactive demo with different stripe configurations:
<p class="text-sm text-muted mb-3">Interactive demo with different stripe configurations:</p>
<div class="mb-4">
<label class="text-sm font-semibold mb-2 block">Choose Stripe Style:</label>
<div class="flex gap-2 flex-wrap">
<button type="button" class="btn btn-sm btn-outline" id="stripe-default">Default</button>
<button type="button" class="btn btn-sm btn-outline" id="stripe-light">Light Grey</button>
<button type="button" class="btn btn-sm btn-outline" id="stripe-medium">Medium Grey</button>
<button type="button" class="btn btn-sm btn-outline" id="stripe-primary">Primary Tint</button>
<button type="button" class="btn btn-sm btn-outline" id="stripe-success">Success Tint</button>
<button type="button" class="btn btn-sm btn-outline" id="stripe-custom">Custom Hex</button>
</div>
</div>
<div id="stripe-demo-table"></div>/**
* Stripe Colour Options - from public/showcase/tables.
*/
export function mount(root) {
const sampleData = [
{
id: 1,
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Admin',
status: 'Active',
joined: '2024-01-15'
},
{id: 2, name: 'Bob Smith', email: 'bob@example.com', role: 'Developer', status: 'Active', joined: '2024-02-20'},
{
id: 3,
name: 'Carol White',
email: 'carol@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-03-10'
},
{
id: 4,
name: 'David Brown',
email: 'david@example.com',
role: 'Manager',
status: 'Inactive',
joined: '2024-01-05'
},
{
id: 5,
name: 'Eva Martinez',
email: 'eva@example.com',
role: 'Developer',
status: 'Active',
joined: '2024-04-01'
},
{
id: 6,
name: 'Frank Wilson',
email: 'frank@example.com',
role: 'Admin',
status: 'Active',
joined: '2024-02-14'
},
{
id: 7,
name: 'Grace Lee',
email: 'grace@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-05-22'
},
{
id: 8,
name: 'Henry Davis',
email: 'henry@example.com',
role: 'Developer',
status: 'Inactive',
joined: '2024-03-30'
},
{id: 9, name: 'Ivy Chen', email: 'ivy@example.com', role: 'Manager', status: 'Active', joined: '2024-06-15'},
{
id: 10,
name: 'Jack Taylor',
email: 'jack@example.com',
role: 'Developer',
status: 'Active',
joined: '2024-04-18'
},
{
id: 11,
name: 'Kate Anderson',
email: 'kate@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-07-01'
},
{id: 12, name: 'Leo Thomas', email: 'leo@example.com', role: 'Admin', status: 'Inactive', joined: '2024-01-28'},
{
id: 13,
name: 'Mia Jackson',
email: 'mia@example.com',
role: 'Developer',
status: 'Active',
joined: '2024-08-10'
},
{
id: 14,
name: 'Noah Garcia',
email: 'noah@example.com',
role: 'Manager',
status: 'Active',
joined: '2024-05-05'
},
{
id: 15,
name: 'Olivia Moore',
email: 'olivia@example.com',
role: 'Designer',
status: 'Active',
joined: '2024-09-20'
}
];
// ============================================
// Stripe Styling Demo
// ============================================
// Sample data for stripe demo
const stripeData = sampleData.slice(0, 6);
// Create initial stripe demo table
let stripeTable = Domma.tables.create('#stripe-demo-table', {
data: stripeData,
columns: [
{key: 'name', title: 'Name'},
{key: 'email', title: 'Email'},
{key: 'role', title: 'Role'}
],
pagination: false,
striped: true
});
// Helper to recreate table with new stripe config
function updateStripeDemo(config) {
const buttons = ['stripe-default', 'stripe-light', 'stripe-medium', 'stripe-primary', 'stripe-success', 'stripe-custom'];
buttons.forEach(id => {
$('#' + id).removeClass('btn-primary').addClass('btn-outline');
});
$('#' + config.activeButton).removeClass('btn-outline').addClass('btn-primary');
stripeTable = Domma.tables.create('#stripe-demo-table', {
data: stripeData,
columns: [
{key: 'name', title: 'Name'},
{key: 'email', title: 'Email'},
{key: 'role', title: 'Role'}
],
pagination: false,
striped: true,
evenRowColor: config.evenRowColor,
oddRowColor: config.oddRowColor,
hoverColor: config.hoverColor
});
}
// Stripe style buttons
$(root).find('#stripe-default').on('click', () => {
updateStripeDemo({
activeButton: 'stripe-default',
evenRowColor: null,
oddRowColor: null,
hoverColor: null
});
});
$(root).find('#stripe-light').on('click', () => {
updateStripeDemo({
activeButton: 'stripe-light',
evenRowColor: 'lighter',
oddRowColor: 'light',
hoverColor: 'medium'
});
});
$(root).find('#stripe-medium').on('click', () => {
updateStripeDemo({
activeButton: 'stripe-medium',
evenRowColor: 'light',
oddRowColor: 'medium',
hoverColor: 'dark'
});
});
$(root).find('#stripe-primary').on('click', () => {
updateStripeDemo({
activeButton: 'stripe-primary',
evenRowColor: 'none',
oddRowColor: 'primary-tint',
hoverColor: 'light'
});
});
$(root).find('#stripe-success').on('click', () => {
updateStripeDemo({
activeButton: 'stripe-success',
evenRowColor: 'none',
oddRowColor: 'success-tint',
hoverColor: 'light'
});
});
$(root).find('#stripe-custom').on('click', () => {
updateStripeDemo({
activeButton: 'stripe-custom',
evenRowColor: '#ffffff',
oddRowColor: '#e8f4f8',
hoverColor: '#d4e9f2'
});
});
}Export Methods
Programmatically export data in various formats.
// Copy to clipboard
table.copyToClipboard(); // Tab-separated (paste into Excel)
table.copyToClipboard('csv'); // CSV format
table.copyToClipboard('json'); // JSON format
// Get data as string
const csv = table.toCSV(); // CSV string
const json = table.toJSON(); // JSON string
const excel = table.toExcel(); // HTML table (Excel-compatible)
// Download files
table.download('csv', 'users.csv');
table.download('excel', 'users.xls');
table.download('json', 'users.json');
// Export callback
table.on('export', (info) => {
console.log(`Exported ${info.rowCount} rows as ${info.format}`);
});
Try the export buttons in the Interactive Demo above, or use these:
<p class="text-sm text-muted mb-3">Try the export buttons in the Interactive Demo above, or use
these:</p>
<div class="flex gap-2 flex-wrap">
<button type="button" class="btn btn-primary btn-sm" id="export-csv">Download CSV</button>
<button type="button" class="btn btn-primary btn-sm" id="export-excel">Download Excel</button>
<button type="button" class="btn btn-primary btn-sm" id="export-json">Download JSON</button>
<button type="button" class="btn btn-outline btn-sm" id="copy-clipboard">Copy to Clipboard</button>
</div>/**
* Export Methods - from public/showcase/tables.
*/
export function mount(root) {
// Export buttons
$(root).find('#export-csv').on('click', () => table.download('csv', 'users.csv'));
$(root).find('#export-excel').on('click', () => table.download('excel', 'users.xls'));
$(root).find('#export-json').on('click', () => table.download('json', 'users.json'));
$(root).find('#copy-clipboard').on('click', async function () {
await table.copyToClipboard();
$(this).text('Copied!').addClass('btn-success').removeClass('btn-outline');
setTimeout(() => {
$(this).text('Copy to Clipboard').removeClass('btn-success').addClass('btn-outline');
}, 1500);
});
}Events
Listen to table events for custom behavior.
// Available events
table.on('sort', (column, direction) => { });
table.on('filter', (activeFilters) => { });
table.on('search', (query) => { });
table.on('page', (page, pageSize) => { });
table.on('select', (rows, indices) => { });
table.on('edit', (row, column, oldValue, newValue) => { });
table.on('render', () => { });
table.on('dataChange', (data) => { });
// Remove event listener
table.off('sort', handler);
// One-time listener
table.once('render', () => {
console.log('Table rendered for the first time');
});
Declarative Table Configuration
While tables use Domma.tables.create(), you can still leverage Domma's config engine to declaratively manage table setups, event handlers, and interactions.
Centralized Table Config
// Define all table configurations in one place
const tableConfigs = {
users: {
selector: '#users-table',
options: {
data: userData,
columns: [
{ key: 'id', title: 'ID', sortable: true },
{ key: 'name', title: 'Name', sortable: true },
{ key: 'email', title: 'Email', sortable: true },
{ key: 'role', title: 'Role', filterable: true }
],
pagination: true,
pageSize: 20,
selectable: true,
exportPanel: true
},
events: {
select: (rows) => {
console.log(`${rows.length} users selected`);
},
edit: (row, column, oldVal, newVal) => {
// Save to server
api.updateUser(row.id, { [column]: newVal });
}
}
},
products: {
selector: '#products-table',
options: {
data: productData,
columns: [
{ key: 'sku', title: 'SKU', sortable: true },
{ key: 'name', title: 'Product', sortable: true },
{ key: 'price', title: 'Price', sortable: true, render: (val) => `$${val}` },
{ key: 'stock', title: 'Stock', sortable: true }
],
pagination: true,
pageSize: 50
}
}
};
// Initialize all tables
Object.values(tableConfigs).forEach(config => {
const table = Domma.tables.create(config.selector, config.options);
// Attach event handlers
if (config.events) {
Object.entries(config.events).forEach(([event, handler]) => {
table.on(event, handler);
});
}
});
Using $.setup() for Table Containers
// Enhance table containers with config engine
$.setup({
'.table-container': {
initial: {
css: {
background: 'var(--dm-gray-50)',
padding: 'var(--dm-space-4)',
borderRadius: 'var(--dm-radius-lg)'
}
},
events: {
ready: (e, $el) => {
// Create table when container is ready
const tableId = $el.find('table').attr('id');
const config = tableConfigs[tableId];
if (config) {
const table = Domma.tables.create(`#${tableId}`, config.options);
$el.data('tableInstance', table);
}
}
}
},
'#export-controls': {
events: {
'click .export-btn': (e, $el) => {
const format = $(e.target).data('format');
const table = $('.table-container').data('tableInstance');
if (table) {
table.download(format);
Domma.elements.toast.success(`Exported as ${format.toUpperCase()}`);
}
}
}
}
});
Dynamic Table Factory
// Create a reusable table factory with config
class TableFactory {
static presets = {
users: {
columns: [
{ key: 'id', title: 'ID', sortable: true },
{ key: 'name', title: 'Name', sortable: true },
{ key: 'email', title: 'Email', sortable: true, editable: true },
{ key: 'role', title: 'Role', filterable: true }
],
pagination: true,
pageSize: 20,
exportPanel: true
},
products: {
columns: [
{ key: 'sku', title: 'SKU', sortable: true },
{ key: 'name', title: 'Product', sortable: true },
{ key: 'price', title: 'Price', sortable: true, render: (val) => `$${val}` },
{ key: 'stock', title: 'Stock', sortable: true }
],
pagination: true,
pageSize: 50
}
};
static create(selector, preset, data, customOptions = {}) {
const presetConfig = this.presets[preset];
if (!presetConfig) {
throw new Error(`Unknown preset: ${preset}`);
}
return Domma.tables.create(selector, {
...presetConfig,
...customOptions,
data
});
}
}
// Usage - clean and declarative
const usersTable = TableFactory.create('#users', 'users', userData);
const productsTable = TableFactory.create('#products', 'products', productData, {
pageSize: 100 // Override preset
});
State-Driven Table Management
// Use Domma models for reactive table state
const tableState = M.create({
data: { type: 'array', default: [] },
sortColumn: { type: 'string', default: '' },
sortDirection: { type: 'string', default: 'asc' },
currentPage: { type: 'number', default: 1 },
pageSize: { type: 'number', default: 20 },
selectedRows: { type: 'array', default: [] }
});
// Create table
const table = Domma.tables.create('#state-table', {
data: tableState.get('data'),
pageSize: tableState.get('pageSize'),
pagination: true
});
// Sync table events with state
table.on('sort', (column, direction) => {
tableState.set({ sortColumn: column, sortDirection: direction });
});
table.on('page', (page) => {
tableState.set({ currentPage: page });
});
table.on('select', (rows) => {
tableState.set({ selectedRows: rows });
});
// React to state changes
tableState.onChange('data', (newData) => {
table.setData(newData);
});
tableState.onChange('selectedRows', (rows) => {
$('#selection-count').text(`${rows.length} selected`);
});
Benefits of Configuration-Driven Tables
- Maintainability - All table configs in centralized location
- Reusability - Share column definitions and presets across tables
- Testability - Easy to test config objects independently
- Type Safety - Config validation before table creation
- State Management - Integrate with reactive models for data flow
Imperative vs Configuration-Driven
Imperative (Scattered)
// Table 1
const table1 = Domma.tables.create('#t1', {
data: data1,
columns: [/*...*/],
pagination: true
});
table1.on('select', (rows) => {
console.log(rows);
});
// Table 2 - elsewhere in code
const table2 = Domma.tables.create('#t2', {
data: data2,
columns: [/*...*/],
pageSize: 50
});
// Configurations spread out
Configuration-Driven (Centralized)
// One config object
const config = {
table1: {
selector: '#t1',
options: {
data: data1,
columns: [/*...*/],
pagination: true
},
events: {
select: (rows) => console.log(rows)
}
},
table2: {
selector: '#t2',
options: { data: data2, pageSize: 50 }
}
};
// Single init
initTables(config);
Complete Method Reference
Data Methods
setData()
getData()
addRow()
updateRow()
removeRow()
refresh()
Sorting Methods
sort()
sortMultiple()
clearSort()
Filtering Methods
search()
filter()
filterBy()
clearFilters()
Pagination Methods
page()
pageSize()
nextPage()
prevPage()
firstPage()
lastPage()
pageInfo()
Selection Methods
select()
deselect()
selectAll()
deselectAll()
toggleSelect()
getSelected()
Column Methods
showColumn()
hideColumn()
toggleColumn()
getColumns()
getVisibleColumns()
getHiddenColumns()
Export Methods
toCSV()
toJSON()
toExcel()
download()
copyToClipboard()
Event Methods
on()
off()
Static Methods
tables.create()
tables.get()
tables.destroy()