@hirehq/smart-table
v1.3.4
Published
A powerful, feature-rich Angular smart table component built with PrimeNG, designed for enterprise applications. This library provides a comprehensive table solution with advanced features like pagination, sorting, filtering, custom templates, and more.
Readme
@hirehq/smart-table
A powerful, feature-rich Angular smart table component built with PrimeNG, designed for enterprise applications. This library provides a comprehensive table solution with advanced features like pagination, sorting, filtering, custom templates, and more.
🚀 Features
- Advanced Table Management: Sortable, filterable, and paginated tables
- Row Selection: Single and multiple selection with checkbox/radio support
- Custom Templates: Built-in templates for badges, profiles, and actions
- Tabbed Interface: Support for multiple data views with URL synchronization
- Responsive Design: Mobile-friendly with adaptive layouts
- TypeScript Support: Full type safety with comprehensive interfaces
- PrimeNG Integration: Built on top of PrimeNG components for consistent UI
- Customizable Styling: Flexible theming and styling options
- Event Handling: Comprehensive event system for user interactions
- Search & Filter: Global search and column-specific filtering
- Export Support: CSV and Excel export capabilities
- Bulk Operations: Support for bulk actions on selected rows
📦 Installation
Prerequisites
Make sure you have the following dependencies in your project:
{
"dependencies": {
"@angular/common": "^19.2.0",
"@angular/core": "^19.2.0",
"@primeuix/themes": "^1.2.3",
"primeng": "^20.0.1"
}
}Install the Library
npm install @hirehq/smart-tableImport in Your Module
import { HirehqSmartTableComponent } from '@hirehq/smart-table';
@Component({
// ... your component config
imports: [HirehqSmartTableComponent],
standalone: true
})🎯 Basic Usage
Simple Table
import { Component } from '@angular/core';
import { HirehqSmartTableComponent, SmartTableConfig, SmartTableColumn } from '@hirehq/smart-table';
@Component({
selector: 'app-my-table',
template: `
<lib-hirehq-smart-table
[config]="tableConfig"
[data]="tableData"
[loading]="isLoading"
(rowClick)="onRowClick($event)"
(selectionChange)="onSelectionChange($event)"
(pageChange)="onPageChange($event)"
></lib-hirehq-smart-table>
`,
imports: [HirehqSmartTableComponent],
standalone: true
})
export class MyTableComponent {
isLoading = false;
tableData = [
{ id: 1, name: 'John Doe', email: '[email protected]', status: 'active' },
{ id: 2, name: 'Jane Smith', email: '[email protected]', status: 'inactive' }
];
tableConfig: SmartTableConfig = {
columns: [
{
label: 'ID',
columnKey: 'id',
sortable: true,
width: '80px'
},
{
label: 'Name',
columnKey: 'name',
sortable: true,
search: { enabled: true, placeholder: 'Search names...' }
},
{
label: 'Email',
columnKey: 'email',
sortable: true
},
{
label: 'Status',
columnKey: 'status',
type: 'badge',
templateMapping: {
badge: {
conditions: [
{ value: 'active', severity: 'success' },
{ value: 'inactive', severity: 'danger' }
]
}
}
}
],
selection: {
enabled: true,
mode: 'multiple',
type: 'checkbox'
},
preferences: {
pagination: {
enabled: true,
pageSize: 10,
pageSizeOptions: [5, 10, 20, 50]
}
}
};
onRowClick(row: any) {
console.log('Row clicked:', row);
}
onSelectionChange(event: any) {
console.log('Selection changed:', event);
}
onPageChange(event: any) {
console.log('Page changed:', event);
}
}⚙️ Configuration
SmartTableConfig Interface
interface SmartTableConfig<T = any> {
columns: SmartTableColumn<T>[];
data?: T[];
loading?: boolean;
title?: string;
showHeader?: boolean;
scrollHeight?: string;
// Row Selection
selection?: {
enabled: boolean;
mode?: 'single' | 'multiple';
type?: 'checkbox' | 'radio';
position?: 'left' | 'right';
frozen?: boolean;
width?: string;
onSelectionChange?: (selectedRows: T[]) => void;
};
// Table Preferences
preferences?: {
pagination?: {
enabled: boolean;
pageSize?: number;
pageSizeOptions?: number[];
showFirstLast?: boolean;
showPageNumbers?: boolean;
totalRecords?: number;
};
globalSearch?: {
enabled: boolean;
placeholder?: string;
onSearch?: (value: string) => void;
};
tabs?: {
enabled: boolean;
tabs?: SmartTableTab[];
defaultTab?: number;
scrollable?: boolean;
showBadges?: boolean;
onClick?: (tab: SmartTableTab, index: number) => void;
};
onSort?: (sortKey: string, direction: 'asc' | 'desc') => void;
};
// Export Actions
exportActions?: {
enabled: boolean;
formats?: ('csv' | 'xlsx')[];
onExport?: (format: 'csv' | 'xlsx', data: T[]) => void;
};
}Column Configuration
interface SmartTableColumn<T = any> {
label: string; // Column header text
columnKey: string; // Data property key
type?: 'badge' | 'profile' | 'action' | 'text' | 'custom' | 'date';
visible?: boolean; // Show/hide column
sortable?: boolean; // Enable sorting
width?: string; // Column width
search?: { // Column-specific search
enabled: boolean;
placeholder?: string;
type?: 'text' | 'select' | 'date';
};
templateMapping?: { // Template-specific configuration
badge?: BadgeConfig;
profile?: ProfileConfig;
action?: ActionConfig;
};
}🎨 Templates
Badge Template
{
label: 'Status',
columnKey: 'status',
type: 'badge',
templateMapping: {
badge: {
conditions: [
{ value: 'active', severity: 'success' },
{ value: 'pending', severity: 'warn' },
{ value: 'inactive', severity: 'danger' }
],
size: 'normal'
}
}
}Profile Template
{
label: 'User',
columnKey: 'user',
type: 'profile',
templateMapping: {
profile: {
image: 'avatar',
title: 'name',
subtitle: 'role',
size: 'normal',
options: {
showImage: true
}
}
}
}Action Template
{
label: 'Actions',
columnKey: 'actions',
type: 'action',
templateMapping: {
action: {
actions: [
{
key: 'view',
label: 'View',
icon: 'pi pi-eye',
severity: 'info',
onClick: (row: any) => this.viewItem(row)
},
{
key: 'edit',
label: 'Edit',
icon: 'pi pi-pencil',
severity: 'secondary',
showMenu: true,
menuItems: [
{
key: 'quick-edit',
label: 'Quick Edit',
icon: 'pi pi-pencil',
onClick: (row: any) => this.quickEdit(row)
},
{
key: 'advanced-edit',
label: 'Advanced Edit',
icon: 'pi pi-cog',
onClick: (row: any) => this.advancedEdit(row)
}
]
},
{
key: 'delete',
label: 'Delete',
icon: 'pi pi-trash',
severity: 'danger',
onClick: (row: any) => this.deleteItem(row)
}
]
}
}
}📱 Tabs Support
@Component({
template: `
<lib-hirehq-smart-table
[config]="tableConfig"
[data]="tableData"
[tabs]="tableTabs"
[defaultTab]="0"
[enableUrlSync]="true"
(tabChange)="onTabChange($event)"
></lib-hirehq-smart-table>
`
})
export class MyComponent {
tableTabs: SmartTableTab[] = [
{ label: 'All Users', value: 0, key: 'all' },
{ label: 'Active Users', value: 1, key: 'active' },
{ label: 'Inactive Users', value: 2, key: 'inactive' }
];
onTabChange(event: { tab: SmartTableTab; index: number }) {
console.log('Tab changed to:', event.tab.key);
// Load data based on selected tab
}
}✅ Selection
The smart table provides comprehensive row selection capabilities with both single and multiple selection modes.
Basic Selection Configuration
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'multiple', // or 'single'
type: 'checkbox', // or 'radio'
position: 'left', // or 'right' (default: 'left')
frozen: true, // Keep selection column visible when scrolling
width: '5rem', // Custom width (default: '4rem')
onSelectionChange: (selectedRows) => {
console.log('Selected rows:', selectedRows);
}
},
columns: [
// ... your columns
]
};Selection Modes
Multiple Selection (Checkbox)
@Component({
template: `
<lib-hirehq-smart-table
[config]="tableConfig"
[data]="tableData"
(selectionChange)="onSelectionChange($event)"
></lib-hirehq-smart-table>
`
})
export class MyComponent {
selectedUsers: any[] = [];
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'multiple',
type: 'checkbox'
},
columns: [
{ label: 'Name', columnKey: 'name' },
{ label: 'Email', columnKey: 'email' },
{ label: 'Status', columnKey: 'status' }
]
};
onSelectionChange(event: SmartTableSelectionEvent) {
this.selectedUsers = event.rows;
console.log('Selection changed:', {
action: event.action, // 'selected' | 'deselected' | 'headerToggle'
selectedRow: event.row,
allSelectedRows: event.rows,
allChecked: event.allChecked
});
}
}Single Selection (Radio)
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'single',
type: 'radio'
},
columns: [
{ label: 'Name', columnKey: 'name' },
{ label: 'Email', columnKey: 'email' }
]
};
onSelectionChange(event: SmartTableSelectionEvent) {
const selectedUser = event.row;
console.log('Selected user:', selectedUser);
}Advanced Selection Configuration
Selection Column Position
Position the selection column on either the left (default) or right side of the table:
// Left-positioned selection column (default)
selection: {
enabled: true,
mode: 'multiple',
position: 'left' // Default position
}
// Right-positioned selection column
selection: {
enabled: true,
mode: 'multiple',
position: 'right'
}Frozen Selection Column
Keep the selection column visible while horizontally scrolling through the table:
selection: {
enabled: true,
mode: 'multiple',
position: 'left',
frozen: true, // Selection column stays visible during horizontal scroll
width: '4rem'
}Custom Selection Column Width
Customize the width of the selection column to fit your design needs:
selection: {
enabled: true,
mode: 'multiple',
width: '6rem', // Custom width (default is '4rem')
position: 'left',
frozen: true
}Complete Advanced Configuration Example
@Component({
template: `
<lib-hirehq-smart-table
[config]="tableConfig"
[data]="wideTableData"
(selectionChange)="onSelectionChange($event)"
></lib-hirehq-smart-table>
`
})
export class WideTableComponent {
wideTableData = [
{
id: 1, name: 'John', email: '[email protected]',
department: 'Engineering', position: 'Senior Developer',
salary: 85000, startDate: '2020-01-15', manager: 'Alice Smith'
},
// ... more data
];
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'multiple',
type: 'checkbox',
position: 'right', // Position on right side
frozen: true, // Keep visible during scroll
width: '5rem' // Slightly wider for better UX
},
scrollHeight: '400px',
columns: [
{ label: 'ID', columnKey: 'id', width: '80px' },
{ label: 'Name', columnKey: 'name', width: '150px' },
{ label: 'Email', columnKey: 'email', width: '200px' },
{ label: 'Department', columnKey: 'department', width: '150px' },
{ label: 'Position', columnKey: 'position', width: '200px' },
{ label: 'Salary', columnKey: 'salary', width: '120px' },
{ label: 'Start Date', columnKey: 'startDate', width: '120px' },
{ label: 'Manager', columnKey: 'manager', width: '150px' }
]
};
onSelectionChange(event: SmartTableSelectionEvent) {
console.log('Selection changed:', event);
// Handle selection with frozen column functionality
}
}Selection Events
The component emits detailed selection events:
interface SmartTableSelectionEvent<T = any> {
rows: T[]; // All currently selected rows
index?: number; // Index of the row that triggered the event
row?: T; // The specific row that was selected/deselected
action?: 'selected' | 'deselected' | 'headerToggle'; // Action type
allChecked?: boolean; // Whether all rows are selected (for header toggle)
}Handling Selection Events
export class UserManagementComponent {
selectedUsers: User[] = [];
onSelectionChange(event: SmartTableSelectionEvent<User>) {
switch (event.action) {
case 'selected':
console.log(`Selected user: ${event.row?.name}`);
this.handleUserSelected(event.row!);
break;
case 'deselected':
console.log(`Deselected user: ${event.row?.name}`);
this.handleUserDeselected(event.row!);
break;
case 'headerToggle':
if (event.allChecked) {
console.log('All users selected');
this.handleAllUsersSelected(event.rows);
} else {
console.log('All users deselected');
this.handleAllUsersDeselected();
}
break;
}
// Update local state
this.selectedUsers = event.rows;
// Enable/disable bulk actions
this.updateBulkActionButtons();
}
private handleUserSelected(user: User) {
// Individual selection logic
}
private handleUserDeselected(user: User) {
// Individual deselection logic
}
private handleAllUsersSelected(users: User[]) {
// Bulk selection logic
}
private handleAllUsersDeselected() {
// Clear selection logic
}
private updateBulkActionButtons() {
// Enable bulk operations based on selection count
const hasSelection = this.selectedUsers.length > 0;
// Update UI state accordingly
}
}Programmatic Selection Control
export class MyComponent {
@ViewChild(HirehqSmartTableComponent) smartTable!: HirehqSmartTableComponent;
// Programmatically select rows
selectSpecificUsers() {
const usersToSelect = this.tableData.filter(user => user.status === 'active');
this.smartTable.selectedRows = usersToSelect;
}
// Clear all selections
clearSelection() {
this.smartTable.selectedRows = [];
}
// Select first row
selectFirstRow() {
if (this.tableData.length > 0) {
this.smartTable.selectedRows = [this.tableData[0]];
}
}
}Selection with Custom Actions
@Component({
template: `
<div class="selection-actions" *ngIf="selectedUsers.length > 0">
<p-button
label="Delete Selected ({{ selectedUsers.length }})"
icon="pi pi-trash"
severity="danger"
(onClick)="deleteSelectedUsers()"
></p-button>
<p-button
label="Export Selected"
icon="pi pi-download"
severity="secondary"
(onClick)="exportSelectedUsers()"
></p-button>
</div>
<lib-hirehq-smart-table
[config]="tableConfig"
[data]="tableData"
(selectionChange)="onSelectionChange($event)"
></lib-hirehq-smart-table>
`
})
export class UserManagementComponent {
selectedUsers: User[] = [];
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'multiple',
type: 'checkbox'
},
// ... columns configuration
};
onSelectionChange(event: SmartTableSelectionEvent<User>) {
this.selectedUsers = event.rows;
}
deleteSelectedUsers() {
if (this.selectedUsers.length === 0) return;
// Confirm deletion
if (confirm(`Delete ${this.selectedUsers.length} users?`)) {
// Perform bulk delete operation
this.userService.deleteUsers(this.selectedUsers.map(u => u.id))
.subscribe(() => {
// Refresh table data
this.loadUsers();
// Clear selection
this.selectedUsers = [];
});
}
}
exportSelectedUsers() {
if (this.selectedUsers.length === 0) return;
// Export selected users
this.exportService.exportUsers(this.selectedUsers, 'selected-users.csv');
}
}Selection Best Practices
- Provide Clear Visual Feedback: Always show the number of selected items
- Bulk Operations: Enable bulk actions when multiple items are selected
- Clear Selection: Provide a way to clear all selections
- Performance: For large datasets, consider virtual scrolling with selection
- Accessibility: Use proper ARIA labels for screen readers
- Frozen Selection: Use frozen selection columns for wide tables to maintain usability
- Position Strategy: Consider positioning selection on the right for tables with many action buttons on the left
- Width Optimization: Adjust selection column width based on your UI density requirements
// Good: Clear visual feedback with advanced selection
template: `
<div class="selection-summary" *ngIf="selectedUsers.length > 0">
{{ selectedUsers.length }} user(s) selected
<p-button label="Clear Selection" (onClick)="clearSelection()"></p-button>
</div>
`
// Good: Wide table with right-positioned frozen selection
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'multiple',
position: 'right', // Better for tables with left-side actions
frozen: true, // Essential for wide tables
width: '4.5rem' // Optimized for checkbox + padding
},
scrollHeight: '500px',
columns: [
// Many columns...
]
};
// Good: Narrow table with standard left selection
tableConfig: SmartTableConfig = {
selection: {
enabled: true,
mode: 'single',
position: 'left', // Standard position for simple tables
width: '3.5rem' // Narrower for radio buttons
},
columns: [
// Few columns...
]
};Selection Configuration Summary
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| enabled | boolean | - | Enable/disable row selection |
| mode | 'single' \| 'multiple' | 'multiple' | Selection mode |
| type | 'checkbox' \| 'radio' | 'checkbox' | Visual selection indicator |
| position | 'left' \| 'right' | 'left' | Position of selection column |
| frozen | boolean | false | Keep selection column visible during scroll |
| width | string | '4rem' | Custom width for selection column |
| onSelectionChange | function | - | Callback function for selection changes |
🔍 Search and Filtering
Global Search
tableConfig: SmartTableConfig = {
preferences: {
globalSearch: {
enabled: true,
placeholder: 'Search all columns...',
onSearch: (value) => {
console.log('Global search:', value);
}
}
},
// ... other config
};Column-Specific Search
{
label: 'Category',
columnKey: 'category',
search: {
enabled: true,
type: 'select',
options: [
{ label: 'Technology', value: 'tech' },
{ label: 'Finance', value: 'finance' },
{ label: 'Healthcare', value: 'healthcare' }
]
}
}📊 Pagination
The library includes a built-in pagination component with the following features:
- Page size selection
- Page navigation (first, previous, next, last)
- Page number display
- Total records count
- Responsive design
tableConfig: SmartTableConfig = {
pagination: {
enabled: true,
pageSize: 20,
pageSizeOptions: [10, 20, 50, 100],
showFirstLast: true,
showPageNumbers: true
}
};🎭 Events
The component emits various events for user interactions:
<lib-hirehq-smart-table
(rowClick)="onRowClick($event)"
(columnClick)="onColumnClick($event)"
(selectionChange)="onSelectionChange($event)"
(pageChange)="onPageChange($event)"
(sortChange)="onSortChange($event)"
(globalSearchChange)="onSearchChange($event)"
(actionClick)="onActionClick($event)"
(tabChange)="onTabChange($event)"
></lib-hirehq-smart-table>🎨 Styling
CSS Variables
The component uses CSS variables for dynamic styling:
.smart-table-root-container {
--smart-table-height: 400px;
--smart-table-border-radius: 8px;
--smart-table-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}Custom Classes
{
label: 'Status',
columnKey: 'status',
cellClass: (row: any) => {
return row.status === 'active' ? 'text-green-600' : 'text-red-600';
},
rowClass: (row: any) => {
return row.isHighlighted ? 'bg-yellow-50' : '';
}
}📱 Responsive Design
The table automatically adapts to different screen sizes:
- Mobile-first approach
- Responsive column hiding
- Touch-friendly interactions
- Adaptive pagination controls
🔧 Advanced Configuration
Custom Templates
{
label: 'Custom',
columnKey: 'custom',
type: 'custom',
customTemplate: YourCustomTemplateComponent
}Frozen Columns
{
label: 'ID',
columnKey: 'id',
frozen: true,
alignFrozen: 'left',
width: '80px'
}Export Configuration
tableConfig: SmartTableConfig = {
export: {
enabled: true,
formats: ['csv', 'xlsx']
}
};🚀 Performance Tips
- Use OnPush Change Detection: The component is optimized for OnPush strategy
- Virtual Scrolling: For large datasets, consider implementing virtual scrolling
- Lazy Loading: Load data in chunks for better performance
- Debounce Search: Implement debouncing for search inputs
🐛 Troubleshooting
Common Issues
- Table not rendering: Check if PrimeNG modules are properly imported
- Styling issues: Ensure PrimeNG themes are included in your project
- Performance problems: Verify data structure and consider pagination
Debug Mode
Enable console logging for debugging:
// The component logs configuration changes to console
console.log('config', val); // Check browser console for config updates📚 Examples
See the following files for comprehensive examples:
src/lib/pagination/example-usage.ts- Pagination examplessrc/lib/templates/action-example-usage.ts- Action template examples
🤝 Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
📄 License
This project is licensed under the MIT License.
🆘 Support
For support and questions:
- Check the documentation
- Review the examples
- Open an issue on GitHub
- Contact the development team
Built with ❤️ by the HireHQ team
