# NgxStPrimengTables

A powerful Angular table component built on PrimeNG Table with advanced features for data display, filtering, sorting, selection, and inline editing.

## Table of Contents
- [Overview](#overview)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Inputs](#inputs)
- [Outputs](#outputs)
- [Models](#models)
- [Features](#features)
- [Examples](#examples)

---

## Overview

The `ngx-st-primeng-table` component extends PrimeNG Table with enterprise features including:
- Local and server-side (lazy) data loading
- Advanced filtering with custom options
- Column selection, reordering, and resizing
- Single and multiple row selection
- Inline row editing
- Persistent table state in localStorage or URL
- Mobile-responsive view
- Custom templates for cells and actions
- Export and pagination

---

## Installation

```bash
npm install ngx-st-primeng-tables primeng primeicons
```

Import the module in your application:

```typescript
import { NgxStPrimengTablesModule } from 'ngx-st-primeng-tables';

@NgModule({
  imports: [
    NgxStPrimengTablesModule
  ]
})
export class AppModule { }
```

---

## Basic Usage

```html
<ngx-st-primeng-table
  [data]="users"
  [columns]="columns"
  [rows]="10"
  tableTitle="Users List">
</ngx-st-primeng-table>
```

```typescript
export class MyComponent {
  users = [
    { id: 1, name: 'John Doe', email: 'john@example.com', active: true },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false }
  ];

  columns: StPrimengTableColumnModel[] = [
    { field: 'id', header: 'ID', type: 'number', width: '80px' },
    { field: 'name', header: 'Name', type: 'string' },
    { field: 'email', header: 'Email', type: 'string' },
    { field: 'active', header: 'Active', type: 'boolean', width: '100px' }
  ];
}
```

---

## Inputs

### Basic Configuration

#### `data`
- **Type:** `any[]` (required)
- **Description:** Array of data objects to display in the table.
- **Example:**
  ```typescript
  [data]="users"
  ```

#### `columns`
- **Type:** `StPrimengTableColumnModel[]` (required)
- **Description:** Column definitions that configure display, sorting, filtering, and editing.
- **Example:**
  ```typescript
  [columns]="columnDefs"
  ```

#### `tableTitle`
- **Type:** `string`
- **Default:** `''`
- **Description:** Title displayed in the table caption.
- **Example:**
  ```typescript
  tableTitle="User Management"
  ```

#### `localTable`
- **Type:** `boolean`
- **Default:** `true`
- **Description:** When true, sorting/filtering handled client-side. When false, enables lazy loading mode.
- **Example:**
  ```typescript
  [localTable]="false"
  [totalRecords]="totalCount"
  (loadData)="onLoadData($event)"
  ```

#### `totalRecords`
- **Type:** `number`
- **Default:** `0`
- **Description:** Total number of records (for server-side pagination). Required when `localTable` is false.
- **Example:**
  ```typescript
  [totalRecords]="1000"
  ```

### Pagination

#### `rows`
- **Type:** `number` (model - two-way binding)
- **Default:** `10`
- **Description:** Number of rows per page.
- **Example:**
  ```typescript
  [(rows)]="pageSize"
  ```

#### `rowsPerPageOptions`
- **Type:** `number[]`
- **Default:** `[5, 10, 20, 25]`
- **Description:** Options for rows per page dropdown.
- **Example:**
  ```typescript
  [rowsPerPageOptions]="[10, 25, 50, 100]"
  ```

### Display & Layout

#### `showGlobalSearch`
- **Type:** `boolean`
- **Default:** `true`
- **Description:** Shows global search input that searches across all columns.
- **Example:**
  ```typescript
  [showGlobalSearch]="true"
  [globalSearchLabel]="'Search all fields'"
  ```

#### `globalSearchLabel`
- **Type:** `string`
- **Default:** `'Search'`
- **Description:** Placeholder text for global search input.
- **Example:**
  ```typescript
  globalSearchLabel="Search users..."
  ```

#### `columnResize`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables column resizing by dragging column borders.
- **Example:**
  ```typescript
  [columnResize]="true"
  ```

#### `columnReorder`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables column reordering by drag and drop.
- **Example:**
  ```typescript
  [columnReorder]="true"
  ```

#### `columnSelect`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables column visibility selection (show/hide columns).
- **Example:**
  ```typescript
  [columnSelect]="true"
  ```

#### `useMobileView`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables responsive mobile view for narrow screens.
- **Example:**
  ```typescript
  [useMobileView]="true"
  ```

### State Persistence

#### `localStorageId`
- **Type:** `string`
- **Default:** `''`
- **Description:** Key for storing table state in localStorage (pagination, sorting, filters, column order). If empty, state is not persisted.
- **Example:**
  ```typescript
  localStorageId="users-table-state"
  ```

#### `bindSearchToUrl`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Stores filters and pagination in URL query parameters. Takes precedence over localStorage.
- **Example:**
  ```typescript
  [bindSearchToUrl]="true"
  ```

### Row Selection

#### `selectable`
- **Type:** `StPrimengTableSelectableOptionsModel | null`
- **Default:** `null`
- **Description:** Configuration object for row selection feature.
- **Example:**
  ```typescript
  [selectable]="selectConfig"

  selectConfig: StPrimengTableSelectableOptionsModel = {
    active: true,
    multiple: true,
    selectedLabel: 'Selected Users',
    selectAllAvailable: true,
    showSelectTotal: true
  };
  ```

#### `selectedRows`
- **Type:** `any | any[]`
- **Description:** Currently selected row(s). Use two-way binding.
- **Example:**
  ```typescript
  [(selectedRows)]="selectedUsers"
  ```

#### `initSelectRows`
- **Type:** `any | any[]`
- **Description:** Initial row selection when table loads.
- **Example:**
  ```typescript
  [initSelectRows]="preSelectedUsers"
  ```

#### `selectedRowChipsContent`
- **Type:** `(row: any) => string`
- **Description:** Function to generate display text for selected row chips.
- **Example:**
  ```typescript
  [selectedRowChipsContent]="formatSelectedChip"

  formatSelectedChip(row: any): string {
    return `${row.name} (${row.email})`;
  }
  ```

### Row Actions

#### `onRowClick`
- **Type:** `(row: any) => void`
- **Description:** Function called when a row is clicked (if selection is not active).
- **Example:**
  ```typescript
  [onRowClick]="navigateToDetail"

  navigateToDetail(row: any): void {
    this.router.navigate(['/users', row.id]);
  }
  ```

#### `addNewButton`
- **Type:** `StPrimengTableAddNewActionModel | null`
- **Default:** `null`
- **Description:** Configuration for "Add New" button in table caption.
- **Example:**
  ```typescript
  [addNewButton]="addNewConfig"

  addNewConfig: StPrimengTableAddNewActionModel = {
    label: 'Add User',
    action: () => this.openCreateDialog()
  };
  ```

### Row Editing

#### `rowEditing`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables inline row editing with save/cancel buttons.
- **Example:**
  ```typescript
  [rowEditing]="true"
  (updateRowEmitter)="onRowUpdate($event)"
  ```

#### `canRowEditByField`
- **Type:** `string`
- **Default:** `''`
- **Description:** Field name in row data that determines if row is editable (truthy = editable).
- **Example:**
  ```typescript
  canRowEditByField="isEditable"
  // Row must have isEditable: true to be edited
  ```

### Filters

#### `filterInCaption`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Displays filter inputs in caption area instead of column headers.
- **Example:**
  ```typescript
  [filterInCaption]="true"
  ```

### Loading State

#### `loading`
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Shows loading spinner overlay on table.
- **Example:**
  ```typescript
  [loading]="isLoading"
  ```

---

## Outputs

### `loadData`
- **Type:** `EventEmitter<LazyLoadEvent>`
- **Description:** Emitted when data needs to be loaded (lazy loading mode). Contains pagination, sorting, and filter information.
- **Example:**
  ```typescript
  (loadData)="onLazyLoad($event)"

  onLazyLoad(event: LazyLoadEvent): void {
    this.loading = true;
    this.userService.getUsers({
      page: event.first! / event.rows!,
      size: event.rows!,
      sortField: event.sortField,
      sortOrder: event.sortOrder,
      filters: event.filters
    }).subscribe(result => {
      this.users = result.data;
      this.totalRecords = result.total;
      this.loading = false;
    });
  }
  ```

### `selectedRowsEmitter`
- **Type:** `EventEmitter<any>`
- **Description:** Emitted when row selection changes.
- **Example:**
  ```typescript
  (selectedRowsEmitter)="onSelectionChange($event)"

  onSelectionChange(selected: any[]): void {
    console.log('Selected rows:', selected);
  }
  ```

### `updateRowEmitter`
- **Type:** `EventEmitter<any>`
- **Description:** Emitted when a row is saved after inline editing.
- **Example:**
  ```typescript
  (updateRowEmitter)="onRowSaved($event)"

  onRowSaved(row: any): void {
    this.userService.updateUser(row).subscribe(() => {
      this.snackBar.success('User updated successfully');
    });
  }
  ```

---

## Models

### StPrimengTableColumnModel

```typescript
interface StPrimengTableColumnModel {
  // Basic Properties
  field: string;                    // Data field name (required)
  header: string;                   // Column header text (required)
  selectColumnLabel?: string;       // Label for column in column selector
  type?: 'string' | 'date' | 'selection' | 'number' | 
         'qty-input' | 'number-input' | 'boolean' | 'custom-template';
  width?: string;                   // CSS width (e.g., '150px', '20%')
  
  // Sorting & Filtering
  sort?: boolean;                   // Enable sorting (default: true)
  filter?: boolean;                 // Enable filtering (default: true)
  customFilterOptions?: {           // Custom dropdown filter
    title: string;
    data: Observable<any[]>;
    optionLabel: string;
    optionValue: string;
  };
  
  // Display
  flexRight?: boolean;              // Align content right
  translateValue?: {                // Map values to display labels
    [value: string]: string;
  };
  customValueDisplay?: (row: any) => string;  // Custom display function
  customTemplate?: TemplateRef<any>;          // Custom cell template
  
  // Actions Column
  actions?: StPrimengTableActionColumnModel[];
  actionsInMenu?: boolean;          // Show actions in dropdown menu
  
  // Editing
  editRowDisabled?: boolean;        // Disable editing for this column
  onValueChanged?: (row: any) => void;  // Callback on value change
}
```

### StPrimengTableActionColumnModel

```typescript
interface StPrimengTableActionColumnModel {
  iconName: string;                 // Material icon name
  iconClass?: string;               // Additional CSS class
  tooltipName: string;              // Tooltip text
  iconColor?: 'primary' | 'warn' | 'accent';
  
  // Action (choose one)
  action?: (row: any) => void;      // Function to call
  url?: string[];                   // Router navigation path
  
  // Conditional Display
  show?: (row: any) => boolean;     // Show/hide based on row data
}
```

### StPrimengTableSelectableOptionsModel

```typescript
interface StPrimengTableSelectableOptionsModel {
  active: boolean;                  // Enable selection
  multiple: boolean;                // Multiple vs single selection
  selectedLabel?: string;           // Label for selected items section
  disableSelectFieldName?: string;  // Field name to disable selection for row
  selectAllAvailable?: boolean;     // Show "Select All" checkbox
  selectionPageOnly?: boolean;      // Select all only on current page
  showSelectTotal?: boolean;        // Show count of selected items
}
```

### StPrimengTableAddNewActionModel

```typescript
interface StPrimengTableAddNewActionModel {
  label: string;                    // Button text
  action: () => void;               // Function to call when clicked
}
```

---

## Features

### Local vs Lazy Loading

**Local Mode** (`localTable: true`):
- All data loaded at once
- Sorting and filtering handled client-side
- Fast for datasets under 1000 rows

**Lazy Mode** (`localTable: false`):
- Data loaded page by page from server
- Sorting and filtering handled server-side
- Required for large datasets

### Column Types

- **string**: Text display and filtering
- **number**: Numeric display with right alignment
- **boolean**: Checkbox display
- **date**: Date formatting and date range filtering
- **qty-input**: Editable quantity input with +/- buttons
- **number-input**: Editable number input
- **custom-template**: Use your own template

### State Persistence

Table automatically saves and restores:
- Current page and page size
- Sort field and direction
- Filter values
- Column order (if reordering enabled)
- Column visibility (if column select enabled)

### Mobile View

When enabled, table switches to card-based layout on small screens for better usability.

---

## Examples

### Basic Table with Sorting and Filtering

```typescript
columns: StPrimengTableColumnModel[] = [
  { 
    field: 'id', 
    header: 'ID', 
    type: 'number', 
    width: '80px',
    sort: true,
    filter: true
  },
  { 
    field: 'name', 
    header: 'Name', 
    type: 'string',
    sort: true,
    filter: true
  },
  { 
    field: 'email', 
    header: 'Email', 
    type: 'string',
    sort: true,
    filter: true
  },
  { 
    field: 'active', 
    header: 'Status', 
    type: 'boolean',
    width: '100px'
  }
];
```

```html
<ngx-st-primeng-table
  [data]="users"
  [columns]="columns"
  [rows]="10"
  [showGlobalSearch]="true"
  tableTitle="Users">
</ngx-st-primeng-table>
```

### Table with Actions Column

```typescript
columns: StPrimengTableColumnModel[] = [
  { field: 'name', header: 'Name', type: 'string' },
  { field: 'email', header: 'Email', type: 'string' },
  {
    field: 'actions',
    header: 'Actions',
    width: '150px',
    flexRight: true,
    actions: [
      {
        iconName: 'edit',
        tooltipName: 'Edit',
        iconColor: 'primary',
        action: (row) => this.editUser(row)
      },
      {
        iconName: 'delete',
        tooltipName: 'Delete',
        iconColor: 'warn',
        action: (row) => this.deleteUser(row),
        show: (row) => row.canDelete  // Conditional display
      },
      {
        iconName: 'visibility',
        tooltipName: 'View Details',
        url: ['/users', 'id']  // Router navigation
      }
    ]
  }
];
```

### Table with Row Selection

```typescript
selectConfig: StPrimengTableSelectableOptionsModel = {
  active: true,
  multiple: true,
  selectedLabel: 'Selected Users',
  selectAllAvailable: true,
  selectionPageOnly: false,
  showSelectTotal: true,
  disableSelectFieldName: 'isArchived'  // Can't select archived users
};

selectedUsers: any[] = [];
```

```html
<ngx-st-primeng-table
  [data]="users"
  [columns]="columns"
  [selectable]="selectConfig"
  [(selectedRows)]="selectedUsers"
  [selectedRowChipsContent]="formatChip"
  (selectedRowsEmitter)="onSelectionChange($event)">
</ngx-st-primeng-table>
```

```typescript
formatChip(row: any): string {
  return `${row.name} (${row.email})`;
}

onSelectionChange(selected: any[]): void {
  console.log(`${selected.length} users selected`);
}
```

### Lazy Loading Table (Server-Side)

```typescript
users: any[] = [];
totalRecords = 0;
loading = false;
```

```html
<ngx-st-primeng-table
  [localTable]="false"
  [data]="users"
  [columns]="columns"
  [totalRecords]="totalRecords"
  [rows]="10"
  [loading]="loading"
  (loadData)="loadUsers($event)">
</ngx-st-primeng-table>
```

```typescript
loadUsers(event: LazyLoadEvent): void {
  this.loading = true;
  
  const params = {
    page: event.first! / event.rows!,
    size: event.rows!,
    sortField: event.sortField as string,
    sortOrder: event.sortOrder === 1 ? 'asc' : 'desc',
    globalFilter: event.globalFilter,
    filters: event.filters
  };
  
  this.userService.getUsers(params).subscribe(response => {
    this.users = response.data;
    this.totalRecords = response.total;
    this.loading = false;
  });
}
```

### Table with Inline Editing

```typescript
columns: StPrimengTableColumnModel[] = [
  { field: 'name', header: 'Name', type: 'string' },
  { field: 'email', header: 'Email', type: 'string' },
  { 
    field: 'quantity', 
    header: 'Quantity', 
    type: 'qty-input',  // Editable quantity with +/- buttons
    onValueChanged: (row) => this.calculateTotal(row)
  },
  { field: 'active', header: 'Active', type: 'boolean' }
];
```

```html
<ngx-st-primeng-table
  [data]="users"
  [columns]="columns"
  [rowEditing]="true"
  canRowEditByField="isEditable"
  (updateRowEmitter)="saveUser($event)">
</ngx-st-primeng-table>
```

```typescript
saveUser(user: any): void {
  this.userService.updateUser(user).subscribe(() => {
    this.snackBar.success('User updated');
  });
}
```

### Table with Custom Filter

```typescript
columns: StPrimengTableColumnModel[] = [
  { field: 'name', header: 'Name', type: 'string' },
  {
    field: 'department',
    header: 'Department',
    type: 'string',
    customFilterOptions: {
      title: 'Filter by Department',
      data: this.departmentService.getDepartments(),  // Observable
      optionLabel: 'name',
      optionValue: 'id'
    }
  }
];
```

### Table with Custom Template

```typescript
columns: StPrimengTableColumnModel[] = [
  { field: 'name', header: 'Name', type: 'string' },
  {
    field: 'status',
    header: 'Status',
    type: 'custom-template',
    customTemplate: this.statusTemplate
  }
];

@ViewChild('statusTemplate') statusTemplate: TemplateRef<any>;
```

```html
<ng-template #statusTemplate let-row="row">
  <span class="status-badge" [ngClass]="row.status">
    {{ row.status | uppercase }}
  </span>
</ng-template>

<ngx-st-primeng-table
  [data]="users"
  [columns]="columns">
</ngx-st-primeng-table>
```

### Table with State Persistence

```html
<!-- Save to localStorage -->
<ngx-st-primeng-table
  [data]="users"
  [columns]="columns"
  localStorageId="my-users-table"
  [columnSelect]="true"
  [columnReorder]="true">
</ngx-st-primeng-table>

<!-- Save to URL (better for sharing links) -->
<ngx-st-primeng-table
  [data]="users"
  [columns]="columns"
  [bindSearchToUrl]="true">
</ngx-st-primeng-table>
```

### Full-Featured Table Example

```typescript
export class UsersComponent {
  users: any[] = [];
  totalRecords = 0;
  loading = false;
  selectedUsers: any[] = [];
  pageSize = 10;

  columns: StPrimengTableColumnModel[] = [
    { field: 'id', header: 'ID', type: 'number', width: '80px', sort: true },
    { field: 'name', header: 'Name', type: 'string', sort: true, filter: true },
    { field: 'email', header: 'Email', type: 'string', sort: true, filter: true },
    { 
      field: 'role', 
      header: 'Role', 
      type: 'string',
      translateValue: {
        'admin': 'Administrator',
        'user': 'Regular User',
        'guest': 'Guest'
      }
    },
    { field: 'active', header: 'Active', type: 'boolean', width: '100px' },
    { field: 'createdAt', header: 'Created', type: 'date', width: '120px' },
    {
      field: 'actions',
      header: '',
      width: '120px',
      flexRight: true,
      actions: [
        {
          iconName: 'edit',
          tooltipName: 'Edit User',
          iconColor: 'primary',
          action: (row) => this.editUser(row)
        },
        {
          iconName: 'delete',
          tooltipName: 'Delete User',
          iconColor: 'warn',
          action: (row) => this.deleteUser(row),
          show: (row) => row.role !== 'admin'
        }
      ]
    }
  ];

  selectConfig: StPrimengTableSelectableOptionsModel = {
    active: true,
    multiple: true,
    selectedLabel: 'Selected Users',
    selectAllAvailable: true,
    showSelectTotal: true
  };

  addNewConfig: StPrimengTableAddNewActionModel = {
    label: 'Add User',
    action: () => this.openCreateDialog()
  };

  loadData(event: LazyLoadEvent): void {
    this.loading = true;
    this.userService.getUsers(event).subscribe(response => {
      this.users = response.data;
      this.totalRecords = response.total;
      this.loading = false;
    });
  }
}
```

```html
<ngx-st-primeng-table
  [localTable]="false"
  [data]="users"
  [columns]="columns"
  [totalRecords]="totalRecords"
  [(rows)]="pageSize"
  [rowsPerPageOptions]="[10, 25, 50, 100]"
  [loading]="loading"
  [showGlobalSearch]="true"
  globalSearchLabel="Search users..."
  tableTitle="User Management"
  [columnResize]="true"
  [columnReorder]="true"
  [columnSelect]="true"
  [selectable]="selectConfig"
  [(selectedRows)]="selectedUsers"
  [addNewButton]="addNewConfig"
  [useMobileView]="true"
  localStorageId="users-table"
  (loadData)="loadData($event)"
  (selectedRowsEmitter)="onSelectionChange($event)">
</ngx-st-primeng-table>
```

---

## Build

Run `ng build ngx-st-primeng-tables` to build the project. The build artifacts will be stored in the `dist/` directory.

## Publishing

After building your library with `ng build ngx-st-primeng-tables`, go to the dist folder `cd dist/ngx-st-primeng-tables` and run `npm publish`.

