npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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-table

Import 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

  1. Provide Clear Visual Feedback: Always show the number of selected items
  2. Bulk Operations: Enable bulk actions when multiple items are selected
  3. Clear Selection: Provide a way to clear all selections
  4. Performance: For large datasets, consider virtual scrolling with selection
  5. Accessibility: Use proper ARIA labels for screen readers
  6. Frozen Selection: Use frozen selection columns for wide tables to maintain usability
  7. Position Strategy: Consider positioning selection on the right for tables with many action buttons on the left
  8. 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

  1. Use OnPush Change Detection: The component is optimized for OnPush strategy
  2. Virtual Scrolling: For large datasets, consider implementing virtual scrolling
  3. Lazy Loading: Load data in chunks for better performance
  4. Debounce Search: Implement debouncing for search inputs

🐛 Troubleshooting

Common Issues

  1. Table not rendering: Check if PrimeNG modules are properly imported
  2. Styling issues: Ensure PrimeNG themes are included in your project
  3. 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 examples
  • src/lib/templates/action-example-usage.ts - Action template examples

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. 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