@avoraui/av-data-table 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,125 +1,42 @@
1
1
  # AvDataTable Component (AvoraUI)
2
2
 
3
- A powerful, customizable Angular data table component with built-in pagination, event-driven actions, and reactive forms support. This component provides flexibility for users to implement custom business logic through event handlers rather than predefined CRUD operations.
3
+ `AvDataTable` is a powerful, customizable standalone Angular component that provides a feature-rich data table interface with advanced pagination, dynamic column rendering, and interactive row actions. It offers a flexible solution for displaying and managing tabular data with support for nested object properties, custom styling, and seamless integration with Angular Reactive Forms.
4
4
 
5
5
  ## Features
6
6
 
7
- - ✅ **Pagination** - Built-in pagination with customizable page sizes
8
- - ✅ **Event-Driven Actions** - Customizable add, modify, and delete event handlers
9
- - ✅ **Reactive Forms Integration** - Implements `ControlValueAccessor`
10
- - ✅ **Flexible Column Configuration** - Support for nested object properties
11
- - ✅ **Customizable Styling** - Configurable alignment and colors
12
- - ✅ **Action Events** - Optional action buttons with customizable event handlers
13
- - ✅ **User-Controlled Operations** - Flexible event system for custom business logic
14
- - ✅ **Responsive Grid Layout** - CSS Grid-based responsive design
15
- - ✅ **Data Synchronization** - Automatic sync between internal and external data sources
7
+ - ✅ **Advanced Pagination** - Configurable page sizes and options.
8
+ - ✅ **Dynamic Grid Layout** - Automatic grid template generation based on column count.
9
+ - ✅ **Nested Property Access** - Support for dot notation (e.g., `user.address.city`).
10
+ - ✅ **Array Handling** - Automatic formatting and joining of array values.
11
+ - ✅ **Interactive Actions** - Configurable modify and delete buttons with customizable event handlers.
12
+ - ✅ **Action Control** - Individual control over button visibility and disabled states.
13
+ - ✅ **Customizable Alignment** - Left, center, or right alignment for headers and data.
14
+ - ✅ **Dynamic Styling** - Custom color support for table cells.
15
+ - ✅ **Event-Driven Architecture** - Emits events for modify, add, and remove operations.
16
+ - ✅ **Auto-Adjustment** - Automatic page recalculation when data changes or items are removed.
17
+ - ✅ **Bidirectional Data Sync** - Intelligent synchronization between internal and external data sources.
18
+ - ✅ **Form Integration** - Implements `ControlValueAccessor` for Reactive Forms compatibility.
19
+ - ✅ **Material Premium UI** - Integration with Angular Material components for a professional look.
20
+ - ✅ **Change Detection Optimized** - Efficient data reference updates to prevent unnecessary re-rendering.
21
+ - ✅ **Null Safety** - Graceful handling of null or undefined values with fallback display.
16
22
 
17
23
  ## Dependencies
18
24
 
19
- This component requires the following Angular Material modules:
20
-
21
- ```typescript
22
- import { MatCard } from "@angular/material/card";
23
- import { MatButton } from "@angular/material/button";
24
- import { MatIcon } from '@angular/material/icon';
25
- ```
26
- ```markdown
27
- 🚨 Version 0.0.4 introduces a breaking change:
28
- Component selector has changed from `aur-data-table` to `av-data-table`.
29
- Please update your templates accordingly.
30
- ```
25
+ The component requires Angular Material. Ensure the following are available in your environment:
26
+ - `@angular/material/card`
27
+ - `@angular/material/button`
28
+ - `@angular/material/icon`
29
+ - `@angular/material/paginator`
30
+ - `@angular/material/menu`
31
31
 
32
32
  ## Installation
33
33
 
34
- 1. Copy the component files to your Angular project
35
- 2. Import the component in your module or standalone component
36
- 3. Ensure you have Angular Material (for pagination) installed:
37
-
38
- ```bash
39
- ng add @angular/material
40
- ```
41
-
42
- ## Basic Usage
43
-
44
- ### 1. Import and Configure
45
-
46
- ```typescript
47
- import { AvDataTable } from './path/to/av-data-table';
48
-
49
- @Component({
50
- selector: 'app-example',
51
- template: `
52
- <av-data-table
53
- [TableHeaders]="headers"
54
- [TableColumns]="columns"
55
- [Data]="tableData"
56
- [PageSize]="10"
57
- [PageSizeOptions]="[5, 10, 25, 50]"
58
- [EnableActionColumn]="true"
59
- [EnableButtonDelete]="true"
60
- [EnableButtonModify]="true"
61
- (onModify)="handleModify($event)"
62
- (onItemRemoved)="handleRemove($event)"
63
- (onNewItemAdded)="handleAdd($event)">
64
- </av-data-table>
65
- `
66
- })
67
- export class ExampleComponent {
68
- // Component implementation
69
- }
70
- ```
71
-
72
- ### 2. Define Headers and Columns
73
-
74
- ```typescript
75
- export interface Headers {
76
- label: string;
77
- align: 'left' | 'center' | 'right';
78
- }
79
-
80
- export interface Columns {
81
- field: string;
82
- align: 'left' | 'center' | 'right';
83
- color?: string;
84
- }
85
- export class ExampleComponent {
86
- headers: Headers[] = [
87
- { label: 'Name', align: 'left' },
88
- { label: 'Email', align: 'left' },
89
- { label: 'Department', align: 'left' },
90
- { label: 'Action', align: 'center' }
91
- ];
92
-
93
- columns: Columns[] = [
94
- { field: 'name', align: 'left', color: '#3182ce' },
95
- { field: 'email', align: 'left' },
96
- { field: 'department.name', align: 'left' }, // Nested property
97
- ];
98
-
99
- tableData = [
100
- {
101
- name: 'Kasun Perera',
102
- email: 'kasun.perera@company.lk',
103
- department: { name: 'Information Technology', code: 'IT' }
104
- },
105
- {
106
- name: 'Nimali Fernando',
107
- email: 'nimali.fernando@company.lk',
108
- department: { name: 'Human Resources', code: 'HR' }
109
- },
110
- {
111
- name: 'Rajitha Silva',
112
- email: 'rajitha.silva@company.lk',
113
- department: { name: 'Finance & Accounting', code: 'FIN' }
114
- },
115
- {
116
- name: 'Chaminda Jayawardena',
117
- email: 'chaminda.j@company.lk',
118
- department: { name: 'Marketing & Sales', code: 'MKT' }
119
- }
120
- ];
121
- }
122
- ```
34
+ 1. Copy the component files into your project.
35
+ 2. Import `AvDataTable` in your standalone component or module.
36
+ 3. Add Angular Material if not already present:
37
+ ```bash
38
+ ng add @angular/material
39
+ ```
123
40
 
124
41
  ## API Reference
125
42
 
@@ -127,191 +44,69 @@ export class ExampleComponent {
127
44
 
128
45
  | Property | Type | Default | Description |
129
46
  |----------|------|---------|-------------|
130
- | `TableHeaders` | `Headers[]` | `[]` | Array of header configurations |
131
- | `TableColumns` | `Columns[]` | `[]` | Array of column configurations |
132
- | `Data` | `any[]` | `[]` | Data source for the table |
133
- | `PageSize` | `number` | `5` | Number of items per page |
134
- | `PageSizeOptions` | `number[]` | `[]` | Available page size options |
135
- | `currentPage` | `number` | `0` | Current active page index |
136
- | `EnableActionColumn` | `boolean` | `false` | Show/hide action column |
137
- | `EnableButtonDelete` | `boolean` | `false` | Enable delete button in actions |
138
- | `EnableButtonModify` | `boolean` | `false` | Enable modify button in actions |
139
- | `DisableRemove` | `boolean` | `false` | Disable remove functionality |
140
- | `DisableModify` | `boolean` | `false` | Disable modify functionality |
47
+ | `TableHeaders` | `Headers[]` | `[]` | Array of header configurations with labels and alignment. |
48
+ | `TableColumns` | `Columns[]` | `[]` | Array of column definitions with fields, alignment, and colors. |
49
+ | `Data` | `any[]` | `[]` | Data source for the table. |
50
+ | `PageSize` | `number` | `5` | Initial number of rows per page. |
51
+ | `PageSizeOptions` | `number[]` | `[]` | Available page size choices for the paginator. |
52
+ | `currentPage` | `number` | `0` | Active page index. |
53
+ | `EnableActionColumn` | `boolean` | `false` | Toggles the visibility of the action column. |
54
+ | `EnableButtonDelete` | `boolean` | `false` | Toggles the delete action button. |
55
+ | `EnableButtonModify` | `boolean` | `false` | Toggles the modify action button. |
56
+ | `DisableRemove` | `boolean` | `false` | Prevents local item removal while still emitting events. |
57
+ | `DisableModify` | `boolean` | `false` | Disables the modify button while still emitting events. |
141
58
 
142
59
  ### Output Events
143
60
 
144
- | Event | Type | Description |
145
- |-------|------|-------------|
146
- | `onModify` | `EventEmitter<{index: number, modifiedItem: any, disabled: boolean}>` | Emitted when modify button is clicked - allows custom handling like opening edit forms, viewing details, etc. |
147
- | `onItemRemoved` | `EventEmitter<{index: number, dataSize: number, removedItem: any, disabled: boolean}>` | Emitted when delete button is clicked - allows custom removal logic, confirmations, API calls, etc. |
148
- | `onNewItemAdded` | `EventEmitter<{index: number, dataSize: number, removedItem: any}>` | Emitted when new item events occur - allows custom add item logic |
61
+ | Event | Payload Type | Description |
62
+ |-------|--------------|-------------|
63
+ | `onModify` | `{index: number, modifiedItem: any, disabled: boolean}` | Emits when the modify button is clicked. |
64
+ | `onItemRemoved` | `{index: number, dataSize: number, removedItem: any, disabled: boolean}` | Emits when an item is removed via the delete button. |
65
+ | `onNewItemAdded` | `{index: number, dataSize: number}` | Emitted when internal data size changes (e.g., via sync). |
149
66
 
150
- ### Interfaces
67
+ ## Technical Implementation
151
68
 
152
- ```typescript
153
- interface Headers {
154
- label: string;
155
- align: 'left' | 'center' | 'right';
156
- }
69
+ - **CSS Grid:** Uses a dynamically calculated `grid-template-columns` for responsive row structure.
70
+ - **Nested Property Resolution:** Recursively traverses objects using dot notation for display.
71
+ - **Data Sync:** Uses efficient reference and length verification for performance and stability.
72
+ - **Form Control:** Implements `NG_VALUE_ACCESSOR` for seamless integration with `ngModel` and `[formControl]`.
157
73
 
158
- interface Columns {
159
- field: string;
160
- align: 'left' | 'center' | 'right';
161
- color?: string;
162
- }
163
- ```
164
-
165
- ## Advanced Usage
166
-
167
- ### Nested Object Properties
168
-
169
- The component supports accessing nested object properties using dot notation:
74
+ ## Usage Example
170
75
 
171
76
  ```typescript
172
- columns: Columns[] = [
173
- { field: 'employee.profile.firstName' },
174
- { field: 'employee.contact.address.city' },
175
- { field: 'employee.roles.name' }, // Will join array values with commas
176
- { field: 'department.name' } // Access nested department name
77
+ // Component
78
+ headers: Headers[] = [
79
+ { label: 'Name', align: 'left' },
80
+ { label: 'Role', align: 'center' }
177
81
  ];
178
- ```
179
82
 
180
- ### Reactive Forms Integration
181
-
182
- The component implements `ControlValueAccessor`, making it compatible with Angular Reactive Forms:
183
-
184
- ```typescript
185
- import { FormBuilder, FormGroup } from '@angular/forms';
186
-
187
- export class ExampleComponent {
188
- form: FormGroup;
189
-
190
- constructor(private fb: FormBuilder) {
191
- this.form = this.fb.group({
192
- tableData: [[]] // Initial empty array
193
- });
194
- }
195
- }
196
- ```
197
-
198
- ```html
199
- <form [formGroup]="form">
200
- <av-data-table
201
- formControlName="tableData"
202
- [TableHeaders]="headers"
203
- [TableColumns]="columns">
204
- </av-data-table>
205
- </form>
206
- ```
207
-
208
- ### Event Handling
209
-
210
- The component provides flexible event handlers that allow you to implement custom business logic:
211
-
212
- ```typescript
213
- handleModify(event: {index: number, modifiedItem: any, disabled: boolean}) {
214
- if (event.disabled) {
215
- console.log('Modify action is disabled');
216
- return;
217
- }
218
-
219
- // Example: Open edit dialog with the selected row data
220
- console.log('Modifying employee:', event.modifiedItem.name);
221
- console.log('Employee data:', event.modifiedItem);
222
-
223
- // You can:
224
- // - Open a modal/dialog for editing
225
- // - Navigate to an edit page
226
- // - Show detailed view
227
- // - Perform any custom logic with the row data
228
- this.openEditDialog(event.modifiedItem, event.index);
229
- }
230
-
231
- handleRemove(event: {index: number, dataSize: number, removedItem: any, disabled: boolean}) {
232
- if (event.disabled) {
233
- console.log('Remove action is disabled');
234
- return;
235
- }
236
-
237
- // Example: Custom confirmation and API call
238
- const employee = event.removedItem;
239
- const confirmed = confirm(`Are you sure you want to remove ${employee.name}?`);
240
-
241
- if (confirmed) {
242
- // You can:
243
- // - Make API calls to delete from server
244
- // - Update other related data
245
- // - Show success/error messages
246
- // - Perform cleanup operations
247
- console.log(`${employee.name} removed from ${employee.department.name}`);
248
- console.log('Remaining employees:', event.dataSize);
249
-
250
- // Example API call
251
- this.employeeService.deleteEmployee(employee.id).subscribe({
252
- next: () => console.log('Successfully deleted from server'),
253
- error: (err) => console.error('Deletion failed:', err)
254
- });
255
- }
256
- }
257
-
258
- handleAdd(event: {index: number, dataSize: number}) {
259
- // Example: Trigger add new employee flow
260
- console.log('Adding new employee...');
261
-
262
- // You can:
263
- // - Open add employee dialog
264
- // - Navigate to create page
265
- // - Show form modal
266
- // - Perform any custom add logic
267
- this.openAddEmployeeDialog();
268
- }
269
- ```
270
-
271
- ### Custom Styling
272
-
273
- The component uses CSS classes that you can override:
274
-
275
- ```css
276
- /* Header alignment */
277
- .header-text-left { text-align: left; }
278
- .header-text-center { text-align: center; }
279
- .header-text-right { text-align: right; }
83
+ columns: Columns[] = [
84
+ { field: 'profile.name', align: 'left', color: '#4361ee' },
85
+ { field: 'role', align: 'center' }
86
+ ];
280
87
 
281
- /* Data cell alignment */
282
- .text-left { text-align: left; }
283
- .text-center { text-align: center; }
284
- .text-right { text-align: right; }
88
+ data = [
89
+ { profile: { name: 'Dileesha' }, role: 'Developer' }
90
+ ];
285
91
 
286
- /* Grid layout customization */
287
- .table-grid {
288
- display: grid;
289
- gap: 1rem;
290
- /* grid-template-columns is set dynamically */
291
- }
92
+ // Template
93
+ <av-data-table
94
+ [TableHeaders]="headers"
95
+ [TableColumns]="columns"
96
+ [Data]="data"
97
+ [EnableActionColumn]="true"
98
+ (onModify)="handleModify($event)">
99
+ </av-data-table>
292
100
  ```
293
101
 
294
- ## Requirements
295
-
296
- - Angular 17+
297
- - Angular Material (for pagination component)
298
- - Modern browser with CSS Grid support
102
+ ## Authorship
299
103
 
300
- ## Browser Support
301
-
302
- - Chrome/Edge 57+
303
- - Firefox 52+
304
- - Safari 10.1+
104
+ - **Author**: Dileesha Ekanayake
105
+ - **Email**: dileesha.r.ekanayake@gmail.com
106
+ - **Created**: 2024
107
+ - **Version**: 0.0.7
108
+ - **Responsibility**: Design, implementation, and documentation of the data table component with Angular Material integration.
305
109
 
306
110
  ## License
307
111
 
308
- This project is licensed under the MIT License - see the LICENSE file for details.
309
-
310
- ## Changelog
311
-
312
- ### v0.0.5
313
- - Initial release
314
- - Basic table functionality with pagination
315
- - CRUD operations support
316
- - Reactive forms integration
317
- - Nested object property access
112
+ This project is licensed under the MIT License.
Binary file
@@ -2,11 +2,100 @@ import * as i0 from '@angular/core';
2
2
  import { EventEmitter, forwardRef, Input, Output, Component } from '@angular/core';
3
3
  import { MatCard, MatCardContent } from '@angular/material/card';
4
4
  import { MatIcon } from '@angular/material/icon';
5
- import { MatIconButton } from '@angular/material/button';
6
- import { NgClass, NgStyle } from '@angular/common';
7
- import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
+ import * as i6 from '@angular/material/button';
6
+ import { MatIconButton, MatButtonModule } from '@angular/material/button';
7
+ import * as i1 from '@angular/common';
8
+ import { CommonModule } from '@angular/common';
9
+ import * as i7 from '@angular/forms';
10
+ import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
8
11
  import { MatPaginator } from '@angular/material/paginator';
12
+ import * as i2 from '@angular/material/menu';
13
+ import { MatMenuModule } from '@angular/material/menu';
14
+ import * as i3 from '@angular/material/form-field';
15
+ import { MatFormFieldModule } from '@angular/material/form-field';
16
+ import * as i4 from '@angular/material/input';
17
+ import { MatInputModule } from '@angular/material/input';
18
+ import * as i5 from '@angular/material/select';
19
+ import { MatSelectModule } from '@angular/material/select';
9
20
 
21
+ /**
22
+ * AvDataTable is a standalone Angular component that provides a feature-rich, customizable
23
+ * data table interface with advanced pagination, dynamic column rendering, and interactive row actions.
24
+ * It offers a flexible and powerful solution for displaying and managing tabular data with support for
25
+ * nested object properties, custom styling, and comprehensive CRUD operations.
26
+ *
27
+ * The component implements ControlValueAccessor to integrate seamlessly with Angular reactive forms,
28
+ * supporting bidirectional data binding and form validation. It features server-side and client-side
29
+ * pagination, dynamic grid layout calculation, configurable action columns, and event-driven architecture
30
+ * for parent component interaction. The table supports nested property access with dot notation, custom
31
+ * column alignment, color customization, and intelligent data synchronization.
32
+ *
33
+ * @template T - The type of items displayed in the table rows.
34
+ *
35
+ * <p><strong>Features</strong></p>
36
+ * <ul>
37
+ * <li>Advanced pagination with configurable page sizes and options</li>
38
+ * <li>Dynamic grid template generation based on column count</li>
39
+ * <li>Nested object property access using dot notation (e.g., 'user.address.city')</li>
40
+ * <li>Array value handling with automatic formatting and joining</li>
41
+ * <li>Configurable action column with modify and delete operations</li>
42
+ * <li>Individual control over action button visibility and disabled states</li>
43
+ * <li>Custom column alignment (left, center, right) for headers and data</li>
44
+ * <li>Dynamic color customization for table cells</li>
45
+ * <li>Event emitters for modify, add, and remove operations</li>
46
+ * <li>Automatic page adjustment when data changes or items are removed</li>
47
+ * <li>Two-way data synchronization between internal and external data sources</li>
48
+ * <li>Angular reactive forms integration via ControlValueAccessor</li>
49
+ * <li>Material Design UI components integration</li>
50
+ * <li>Change detection optimization with explicit data reference updates</li>
51
+ * <li>Graceful handling of null/undefined values with fallback display</li>
52
+ * </ul>
53
+ *
54
+ * <p><strong>Technical Implementation</strong></p>
55
+ * <ul>
56
+ * <li>Dynamic CSS Grid layout calculation for responsive column sizing</li>
57
+ * <li>Paginated data slicing with index transformation for accurate positioning</li>
58
+ * <li>Nested property resolution with array mapping and filtering</li>
59
+ * <li>Data synchronization using efficient object reference and length comparison</li>
60
+ * <li>Event-driven communication pattern with parent components</li>
61
+ * <li>Lifecycle management with OnInit and OnChanges implementations</li>
62
+ * <li>Conditional action execution based on disabled state flags</li>
63
+ * <li>Automatic pagination state management and validation</li>
64
+ * </ul>
65
+ *
66
+ * <p><strong>Event Handling</strong></p>
67
+ * <ul>
68
+ * <li><b>onModify:</b> Emits when a row modification is requested, includes index, item data, and disabled state</li>
69
+ * <li><b>onNewItemAdded:</b> Emits when a new item is added to the table</li>
70
+ * <li><b>onItemRemoved:</b> Emits when an item is removed, includes index, data size, removed item, and disabled state</li>
71
+ * </ul>
72
+ *
73
+ * <p><strong>Configuration Options</strong></p>
74
+ * <ul>
75
+ * <li>PageSize: Number of rows per page (default: 5)</li>
76
+ * <li>PageSizeOptions: Array of available page size options</li>
77
+ * <li>TableHeaders: Column header definitions with labels and alignment</li>
78
+ * <li>TableColumns: Column data definitions with field names, alignment, and colors</li>
79
+ * <li>EnableActionColumn: Toggle visibility of action column</li>
80
+ * <li>EnableButtonDelete: Control delete button visibility</li>
81
+ * <li>EnableButtonModify: Control modify button visibility</li>
82
+ * <li>DisableRemove: Prevent item removal while maintaining event emission</li>
83
+ * <li>DisableModify: Prevent item modification while maintaining event emission</li>
84
+ * </ul>
85
+ *
86
+ * <p><strong>Authorship</strong></p>
87
+ * <ul>
88
+ * <li><b>Author:</b> Dileesha Ekanayake</li>
89
+ * <li><b>Email:</b> dileesha.r.ekanayake@gmail.com</li>
90
+ * <li><b>Created:</b> 2024</li>
91
+ * <li><b>Version:</b> 1.0.0</li>
92
+ * <li><b>Responsibility:</b> Design, implementation, and documentation of the data table component
93
+ * with Angular Material integration. Provides comprehensive table functionality including
94
+ * pagination, nested property access, dynamic styling, CRUD operations, and seamless form
95
+ * control integration with reactive forms support. Ensures robust data synchronization,
96
+ * event-driven architecture, and flexible configuration options for diverse use cases.</li>
97
+ * </ul>
98
+ */
10
99
  class AvDataTable {
11
100
  PageSize = 5;
12
101
  PageSizeOptions = [];
@@ -24,6 +113,12 @@ class AvDataTable {
24
113
  DisableRemove = false;
25
114
  DisableModify = false;
26
115
  gridTemplateColumns = '';
116
+ // New features state
117
+ sortField = '';
118
+ sortDirection = '';
119
+ globalSearchTerm = '';
120
+ columnFilters = new Map();
121
+ processedData = [];
27
122
  onChange = () => { };
28
123
  onTouched = () => { };
29
124
  constructor() {
@@ -43,6 +138,7 @@ class AvDataTable {
43
138
  this.initializePaginator();
44
139
  this.calculateGridTemplate();
45
140
  this.syncDataSources();
141
+ this.applyAllFilters();
46
142
  }
47
143
  /**
48
144
  * Initializes the paginator configuration. Sets default values for PageSize, PageSizeOptions,
@@ -83,6 +179,7 @@ class AvDataTable {
83
179
  // Listen for changes in the external Data input
84
180
  if (changes['Data'] && !changes['Data'].firstChange) {
85
181
  this.syncDataSources();
182
+ this.applyAllFilters();
86
183
  // Reset to first page when data changes
87
184
  this.currentPage = 0;
88
185
  }
@@ -90,7 +187,7 @@ class AvDataTable {
90
187
  if (changes['PageSize'] || changes['PageSizeOptions'] || changes['currentPage']) {
91
188
  this.initializePaginator();
92
189
  // Validate current page doesn't exceed available pages
93
- const totalPages = Math.ceil(this.TableData.length / this.PageSize);
190
+ const totalPages = Math.ceil(this.processedData.length / this.PageSize);
94
191
  if (this.currentPage >= totalPages && totalPages > 0) {
95
192
  this.currentPage = totalPages - 1;
96
193
  }
@@ -104,7 +201,7 @@ class AvDataTable {
104
201
  getPaginatedData() {
105
202
  const startIndex = this.currentPage * this.PageSize;
106
203
  const endIndex = startIndex + this.PageSize;
107
- return this.TableData.slice(startIndex, endIndex);
204
+ return this.processedData.slice(startIndex, endIndex);
108
205
  }
109
206
  /**
110
207
  * Handles changes in the pagination state, such as changing the current page or the page size.
@@ -123,7 +220,8 @@ class AvDataTable {
123
220
  * @return {number} The actual index in the entire dataset.
124
221
  */
125
222
  getActualIndex(paginatedIndex) {
126
- return (this.currentPage * this.PageSize) + paginatedIndex;
223
+ const item = this.getPaginatedData()[paginatedIndex];
224
+ return item ? this.TableData.indexOf(item) : -1;
127
225
  }
128
226
  /**
129
227
  * Synchronizes external data source with the internal TableData.
@@ -133,17 +231,10 @@ class AvDataTable {
133
231
  * @return {void} This method does not return a value.
134
232
  */
135
233
  syncDataSources() {
136
- // If external Data is provided and differs from TableData, update TableData
137
- if (this.Data && this.Data.length > 0) {
138
- // Check if Data and TableData are different
139
- const dataString = JSON.stringify(this.Data);
140
- const tableDataString = JSON.stringify(this.TableData);
141
- if (dataString !== tableDataString) {
142
- this.TableData = [...this.Data];
143
- // console.log("Changed Data : " + this.Data);
144
- // Notify the form about the change
145
- this.onChange(this.TableData);
146
- }
234
+ if (this.Data && (this.TableData.length !== this.Data.length || this.TableData !== this.Data)) {
235
+ this.TableData = [...this.Data];
236
+ this.applyAllFilters();
237
+ this.onChange(this.TableData);
147
238
  }
148
239
  }
149
240
  /**
@@ -184,8 +275,8 @@ class AvDataTable {
184
275
  if (this.EnableActionColumn) {
185
276
  const dataColumns = columnsCount - 1; // Subtract action column
186
277
  this.gridTemplateColumns = dataColumns > 0
187
- ? `repeat(${dataColumns}, 1fr) 100px`
188
- : '100px';
278
+ ? `repeat(${dataColumns}, 1fr) 140px`
279
+ : '140px';
189
280
  }
190
281
  else {
191
282
  this.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;
@@ -327,6 +418,7 @@ class AvDataTable {
327
418
  else {
328
419
  this.updateFormValue();
329
420
  }
421
+ this.applyAllFilters();
330
422
  this.onItemRemoved.emit({
331
423
  index: actualIndex,
332
424
  dataSize: this.TableData.length,
@@ -388,14 +480,127 @@ class AvDataTable {
388
480
  writeValue(value) {
389
481
  if (value) {
390
482
  this.TableData = [...value];
483
+ this.applyAllFilters();
391
484
  // Reset to first page when new data is written
392
485
  this.currentPage = 0;
393
486
  }
394
487
  else {
395
488
  this.TableData = [];
489
+ this.processedData = [];
396
490
  this.currentPage = 0;
397
491
  }
398
492
  }
493
+ // Sorting Logic
494
+ toggleSort(field) {
495
+ if (this.sortField === field) {
496
+ if (this.sortDirection === 'asc') {
497
+ this.sortDirection = 'desc';
498
+ }
499
+ else if (this.sortDirection === 'desc') {
500
+ this.sortDirection = '';
501
+ this.sortField = '';
502
+ }
503
+ else {
504
+ this.sortDirection = 'asc';
505
+ }
506
+ }
507
+ else {
508
+ this.sortField = field;
509
+ this.sortDirection = 'asc';
510
+ }
511
+ this.applyAllFilters();
512
+ }
513
+ // Filtering Logic
514
+ applyAllFilters() {
515
+ let data = [...this.TableData];
516
+ // 1. Global Search
517
+ if (this.globalSearchTerm) {
518
+ const term = this.globalSearchTerm.toLowerCase();
519
+ data = data.filter(item => {
520
+ return this.TableColumns.some(col => {
521
+ const val = this.getValueForColumn(item, col);
522
+ return val?.toString().toLowerCase().includes(term);
523
+ });
524
+ });
525
+ }
526
+ // 2. Column-wise Filters
527
+ this.columnFilters.forEach((filter, field) => {
528
+ if (filter.rules.length > 0) {
529
+ data = data.filter(item => {
530
+ const val = (this.getValueForColumn(item, { field, align: 'left' }) || '').toString().toLowerCase();
531
+ const ruleResults = filter.rules.map(rule => {
532
+ const ruleVal = rule.value.toLowerCase();
533
+ switch (rule.operator) {
534
+ case 'starts': return val.startsWith(ruleVal);
535
+ case 'contains': return val.includes(ruleVal);
536
+ case 'not_contains': return !val.includes(ruleVal);
537
+ case 'ends': return val.endsWith(ruleVal);
538
+ case 'equals': return val === ruleVal;
539
+ case 'not_equals': return val !== ruleVal;
540
+ default: return true;
541
+ }
542
+ });
543
+ return filter.matchMode === 'all'
544
+ ? ruleResults.every(r => r)
545
+ : ruleResults.some(r => r);
546
+ });
547
+ }
548
+ });
549
+ // 3. Sorting
550
+ if (this.sortField && this.sortDirection) {
551
+ data.sort((a, b) => {
552
+ const valA = this.getValueForColumn(a, { field: this.sortField, align: 'left' });
553
+ const valB = this.getValueForColumn(b, { field: this.sortField, align: 'left' });
554
+ if (valA < valB)
555
+ return this.sortDirection === 'asc' ? -1 : 1;
556
+ if (valA > valB)
557
+ return this.sortDirection === 'asc' ? 1 : -1;
558
+ return 0;
559
+ });
560
+ }
561
+ this.processedData = data;
562
+ // Adjust pagination if current page is out of bounds
563
+ const totalPages = Math.ceil(this.processedData.length / this.PageSize);
564
+ if (this.currentPage >= totalPages && totalPages > 0) {
565
+ this.currentPage = totalPages - 1;
566
+ }
567
+ }
568
+ clearAllFilters() {
569
+ this.globalSearchTerm = '';
570
+ this.columnFilters.clear();
571
+ this.sortField = '';
572
+ this.sortDirection = '';
573
+ this.applyAllFilters();
574
+ }
575
+ updateGlobalSearch(event) {
576
+ this.globalSearchTerm = event.target.value;
577
+ this.applyAllFilters();
578
+ }
579
+ // Column filter management
580
+ setColumnFilter(field, filter) {
581
+ if (!filter || filter.rules.length === 0) {
582
+ this.columnFilters.delete(field);
583
+ }
584
+ else {
585
+ this.columnFilters.set(field, filter);
586
+ }
587
+ this.applyAllFilters();
588
+ }
589
+ getColumnFilter(field) {
590
+ let filter = this.columnFilters.get(field);
591
+ if (!filter) {
592
+ filter = { matchMode: 'all', rules: [{ operator: 'starts', value: '' }] };
593
+ this.columnFilters.set(field, filter);
594
+ }
595
+ return filter;
596
+ }
597
+ // Check if a column has an active filter with actual values (stable during change detection)
598
+ hasActiveFilter(field) {
599
+ const filter = this.columnFilters.get(field);
600
+ if (!filter)
601
+ return false;
602
+ return filter.rules.some(rule => rule.value !== '');
603
+ }
399
604
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AvDataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
400
605
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.2", type: AvDataTable, isStandalone: true, selector: "av-data-table", inputs: { PageSize: "PageSize", PageSizeOptions: "PageSizeOptions", currentPage: "currentPage", TableHeaders: "TableHeaders", TableColumns: "TableColumns", Data: "Data", EnableActionColumn: "EnableActionColumn", EnableButtonDelete: "EnableButtonDelete", EnableButtonModify: "EnableButtonModify", DisableRemove: "DisableRemove", DisableModify: "DisableModify" }, outputs: { onModify: "onModify", onNewItemAdded: "onNewItemAdded", onItemRemoved: "onItemRemoved" }, providers: [
401
606
  {
@@ -403,7 +608,7 @@ class AvDataTable {
403
608
  useExisting: forwardRef(() => AvDataTable),
404
609
  multi: true
405
610
  }
406
- ], usesOnChanges: true, ngImport: i0, template: "@if (TableData.length > 0) {\r\n <div class=\"item-header-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (header of getDisplayedHeaders(); track $index) {\r\n <strong [ngClass]=\"getHeaderFieldClasses(header)\">\r\n {{ header.label }}\r\n </strong>\r\n }\r\n </div>\r\n\r\n <!-- Display only the paginated items -->\r\n @for (item of getPaginatedData(); track $index) {\r\n <mat-card>\r\n <mat-card-content>\r\n <div class=\"item-details\">\r\n <div class=\"item-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (column of TableColumns; track column) {\r\n <span [ngClass]=\"getDataFieldClasses(column)\"\r\n [ngStyle]=\"{ color: getDataFiledColor(column) }\"\r\n >{{ getValueForColumn(item, column) }}</span>\r\n }\r\n @if (EnableActionColumn) {\r\n <div class=\"action-buttons\">\r\n @if (EnableButtonModify) {\r\n <button\r\n class=\"item-action-edit\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"modifyItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">edit</mat-icon>\r\n </button>\r\n }\r\n @if (EnableButtonDelete) {\r\n <button\r\n class=\"item-action-delete\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"removeItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n </mat-card>\r\n }\r\n\r\n <!-- Move paginator outside the loop -->\r\n <div class=\"paginator-container\">\r\n <mat-paginator\r\n [pageSize]=\"PageSize\"\r\n [length]=\"TableData.length\"\r\n [pageSizeOptions]=\"PageSizeOptions\"\r\n [pageIndex]=\"currentPage\"\r\n (page)=\"pageChange($event)\"\r\n showFirstLastButtons>\r\n </mat-paginator>\r\n </div>\r\n}\r\n", styles: [".mat-header-cell{font-weight:700}mat-card{width:100%;background:none;border:unset;box-shadow:unset;margin:.2rem}mat-card-header{border-top-left-radius:10px;border-top-right-radius:10px;padding-bottom:15px;border-bottom:black solid 1px}mat-card-content{border-radius:unset!important;padding-top:15px;background:unset;overflow:hidden;height:100%}button:disabled{opacity:.5;cursor:not-allowed!important}mat-icon{color:#fff!important;font-size:18px;width:18px;height:18px}.item-header-row{display:grid;padding:12px 20px;background:#f5f7fa;border-bottom:2px solid #e0e4e8;border-top:2px solid #e0e4e8;margin-top:15px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;letter-spacing:.3px}.item-header-row strong{font-weight:600;color:#4a5568;font-size:14px;text-transform:uppercase}.header-text-left{text-align:left;padding-left:16px!important}.header-text-center{text-align:center}.header-text-right{text-align:right}.text-left{text-align:left;padding-left:5px!important;font-weight:500}.text-center{text-align:center;font-weight:500}.text-right{text-align:right}.item-details{width:100%;margin-bottom:-13px!important;background-color:#fff;box-shadow:0 2px 8px #0000000d;overflow:hidden;transition:all .2s ease}.item-row{width:100%;display:grid;align-items:center;padding:10px}.action-buttons{display:flex;flex-direction:row;align-items:center;justify-content:center;margin-left:25px;align-self:center;gap:5px}.item-action-delete{text-align:right!important;margin-right:60px!important;background-color:#f56565!important;height:36px!important;width:36px!important;border-radius:50%!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 4px #f5656533!important}.item-action-edit{justify-self:center;text-align:right!important;background-color:#65a0f6!important;height:36px!important;width:36px!important;border-radius:50%!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 4px #f5656533!important}.item-action-edit mat-icon{color:#fff!important}.item-action{text-align:right!important;margin-right:60px!important;background-color:#f56565!important;height:36px!important;width:36px!important;border-radius:50%!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 4px #f5656533!important}.paginator-container mat-paginator{background:transparent!important;border-top:1px solid #f1f1f1!important}\n"], dependencies: [{ kind: "component", type: MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: MatCardContent, selector: "mat-card-content" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }] });
611
+ ], usesOnChanges: true, ngImport: i0, template: "<!-- Global Search and Clear -->\r\n<div class=\"table-top-actions\">\r\n <button mat-stroked-button (click)=\"clearAllFilters()\" class=\"clear-filters-btn\">\r\n <mat-icon>filter_alt_off</mat-icon> Clear\r\n </button>\r\n <div class=\"spacer\"></div>\r\n <mat-form-field appearance=\"outline\" class=\"global-search-field\">\r\n <mat-icon matIconPrefix>search</mat-icon>\r\n <mat-label>Keyboard Search</mat-label>\r\n <input matInput [(ngModel)]=\"globalSearchTerm\" (input)=\"applyAllFilters()\" placeholder=\"Search...\">\r\n </mat-form-field>\r\n</div>\r\n\r\n@if (processedData.length > 0) {\r\n<div class=\"item-header-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (header of getDisplayedHeaders(); track $index) {\r\n <div class=\"header-cell\" [ngClass]=\"getHeaderFieldClasses(header)\">\r\n <div class=\"header-content\" (click)=\"TableColumns[$index] ? toggleSort(TableColumns[$index].field) : null\">\r\n <strong>{{ header.label }}</strong>\r\n @if (TableColumns[$index]) {\r\n <mat-icon class=\"sort-icon\" [ngClass]=\"{'active': sortField === TableColumns[$index].field}\">\r\n @if (sortField !== TableColumns[$index].field || sortDirection === '') {\r\n unfold_more\r\n } @else if (sortDirection === 'asc') {\r\n expand_less\r\n } @else {\r\n expand_more\r\n }\r\n </mat-icon>\r\n }\r\n </div>\r\n\r\n @if (TableColumns[$index]) {\r\n <button mat-icon-button [matMenuTriggerFor]=\"filterMenu\" class=\"filter-btn\">\r\n <mat-icon [class.active-filter]=\"hasActiveFilter(TableColumns[$index].field)\">filter_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #filterMenu=\"matMenu\" class=\"filter-menu-panel\">\r\n <div class=\"filter-container\" (click)=\"$event.stopPropagation()\">\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <mat-select [(ngModel)]=\"getColumnFilter(TableColumns[$index].field).matchMode\">\r\n <mat-option value=\"all\">Match All</mat-option>\r\n <mat-option value=\"any\">Match Any</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n @for (rule of getColumnFilter(TableColumns[$index].field).rules; track rule; let ri = $index) {\r\n <div class=\"filter-rule\">\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <mat-select [(ngModel)]=\"rule.operator\">\r\n <mat-option value=\"starts\">Starts with</mat-option>\r\n <mat-option value=\"contains\">Contains</mat-option>\r\n <mat-option value=\"not_contains\">Not contains</mat-option>\r\n <mat-option value=\"ends\">Ends with</mat-option>\r\n <mat-option value=\"equals\">Equals</mat-option>\r\n <mat-option value=\"not_equals\">Not equals</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <input matInput [(ngModel)]=\"rule.value\" placeholder=\"Value\">\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <button mat-button color=\"primary\" class=\"add-rule-btn\"\r\n (click)=\"getColumnFilter(TableColumns[$index].field).rules.push({operator: 'starts', value: ''})\">\r\n <mat-icon>add</mat-icon> Add Rule\r\n </button>\r\n\r\n <div class=\"filter-actions\">\r\n <button mat-button\r\n (click)=\"getColumnFilter(TableColumns[$index].field).rules = [{operator: 'starts', value: ''}]; setColumnFilter(TableColumns[$index].field, null)\">Clear</button>\r\n <div class=\"spacer\"></div>\r\n <button mat-flat-button color=\"primary\"\r\n (click)=\"setColumnFilter(TableColumns[$index].field, getColumnFilter(TableColumns[$index].field))\">Apply</button>\r\n </div>\r\n </div>\r\n </mat-menu>\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Display only the paginated items -->\r\n@for (item of getPaginatedData(); track $index) {\r\n<mat-card>\r\n <mat-card-content>\r\n <div class=\"item-details\">\r\n <div class=\"item-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (column of TableColumns; track column) {\r\n <span [ngClass]=\"getDataFieldClasses(column)\" [ngStyle]=\"{ color: getDataFiledColor(column) }\">{{\r\n getValueForColumn(item, column) }}</span>\r\n }\r\n @if (EnableActionColumn) {\r\n <div class=\"action-buttons\">\r\n @if (EnableButtonModify) {\r\n <button class=\"item-action-edit\" mat-icon-button color=\"warn\" (click)=\"modifyItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">edit</mat-icon>\r\n </button>\r\n }\r\n @if (EnableButtonDelete) {\r\n <button class=\"item-action-delete\" mat-icon-button color=\"warn\" (click)=\"removeItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n</mat-card>\r\n}\r\n\r\n<!-- Move paginator outside the loop -->\r\n<div class=\"paginator-container\">\r\n <mat-paginator [pageSize]=\"PageSize\" [length]=\"processedData.length\" [pageSizeOptions]=\"PageSizeOptions\"\r\n [pageIndex]=\"currentPage\" (page)=\"pageChange($event)\" showFirstLastButtons>\r\n </mat-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"no-data-msg\">\r\n No items found matching your search/filters.\r\n</div>\r\n}", styles: [":host{--av-primary: #4361ee;--av-primary-light: #eef1ff;--av-primary-hover: #3651d4;--av-accent: #f72585;--av-accent-light: #fee2ef;--av-edit: #4cc9f0;--av-edit-hover: #3ab8df;--av-delete: #ef476f;--av-delete-hover: #d63d62;--av-text-primary: #1e293b;--av-text-secondary: #64748b;--av-text-muted: #94a3b8;--av-bg-surface: #ffffff;--av-bg-muted: #f8fafc;--av-border: #e2e8f0;--av-border-hover: #cbd5e1;--av-shadow-sm: 0 1px 3px rgba(0, 0, 0, .06);--av-shadow-md: 0 4px 12px rgba(0, 0, 0, .08);--av-shadow-lg: 0 8px 24px rgba(0, 0, 0, .1);--av-radius-sm: 6px;--av-radius-md: 10px;--av-radius-lg: 14px;--av-transition: .25s cubic-bezier(.4, 0, .2, 1);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}mat-card{width:100%;background:none;border:unset;box-shadow:unset;margin:0;padding:0}mat-card-content{border-radius:unset!important;padding:0!important;background:unset;overflow:hidden}mat-icon{font-size:18px;width:18px;height:18px}button:disabled{opacity:.4;cursor:not-allowed!important;filter:grayscale(.3)}.table-top-actions{display:flex;align-items:center;padding:12px 4px;gap:12px}.spacer{flex:1}.clear-filters-btn{border-radius:var(--av-radius-sm)!important;color:var(--av-text-secondary)!important;border-color:var(--av-border)!important;font-weight:500!important;font-size:13px!important;letter-spacing:.3px;transition:all var(--av-transition)!important;padding:0 14px!important;height:38px!important}.clear-filters-btn:hover{background:var(--av-accent-light)!important;color:var(--av-accent)!important;border-color:var(--av-accent)!important;box-shadow:0 2px 8px #f725851f}.clear-filters-btn mat-icon{margin-right:5px;font-size:17px;width:17px;height:17px;transition:color var(--av-transition)}.clear-filters-btn:hover mat-icon{color:var(--av-accent)!important}.global-search-field mat-icon{color:var(--av-text-muted);transition:color var(--av-transition)}::ng-deep .global-search-field .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .global-search-field .mat-mdc-form-field-icon-prefix mat-icon,::ng-deep .global-search-field .mat-mdc-form-field-prefix mat-icon{font-size:20px;width:20px;color:var(--av-text-muted)}::ng-deep .global-search-field .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,::ng-deep .global-search-field .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch,::ng-deep .global-search-field .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-color:var(--av-border)!important}::ng-deep .global-search-field.mat-focused .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,::ng-deep .global-search-field.mat-focused .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch,::ng-deep .global-search-field.mat-focused .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-color:var(--av-primary)!important}.item-header-row{display:grid;padding:0 20px;background:linear-gradient(135deg,#f1f5f9,#e8edf3);border-bottom:2px solid var(--av-primary);border-radius:var(--av-radius-md) var(--av-radius-md) 0 0;margin-top:8px;font-family:inherit;letter-spacing:.4px;min-height:48px;align-items:center}.item-header-row strong{font-weight:700;color:var(--av-text-primary);font-size:12px;text-transform:uppercase;letter-spacing:.8px}.header-text-left{text-align:left;padding-left:16px!important}.header-text-center{text-align:center}.header-text-right{text-align:right}.text-left{text-align:left;padding-left:5px!important;font-weight:500}.text-center{text-align:center;font-weight:500}.text-right{text-align:right}.header-cell{display:flex!important;align-items:center;justify-content:inherit;gap:2px}.header-content{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;padding:4px 6px;border-radius:var(--av-radius-sm);transition:background var(--av-transition)}.header-content:hover{background:#4361ee14}.header-content:active{background:#4361ee26}.sort-icon{font-size:16px;width:16px;height:16px;margin-left:4px;color:var(--av-text-muted);transition:all var(--av-transition);opacity:.5}.header-content:hover .sort-icon{opacity:1;color:var(--av-primary)}.sort-icon.active{color:var(--av-primary)!important;opacity:1!important;animation:sortBounce .3s ease}@keyframes sortBounce{0%{transform:scale(1)}50%{transform:scale(1.3)}to{transform:scale(1)}}.filter-btn{width:30px!important;height:30px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important;border-radius:var(--av-radius-sm)!important;transition:all var(--av-transition)!important}.filter-btn:hover{background:#4361ee14!important}.filter-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--av-text-muted);transition:all var(--av-transition)}.filter-btn:hover mat-icon{color:var(--av-primary)}.active-filter{color:var(--av-primary)!important;position:relative}.item-details{width:100%;margin-bottom:0!important;background-color:var(--av-bg-surface);border-left:3px solid transparent;border-bottom:1px solid var(--av-border);transition:all var(--av-transition);cursor:default}.item-details:hover{background:linear-gradient(90deg,var(--av-primary-light) 0%,var(--av-bg-surface) 40%);border-left-color:var(--av-primary);box-shadow:var(--av-shadow-sm)}.item-row{width:100%;display:grid;align-items:center;padding:14px 24px;font-size:14px;color:var(--av-text-primary);line-height:1.5;overflow:visible}.item-row span{transition:color var(--av-transition)}.action-buttons{display:flex;flex-direction:row;align-items:center;justify-content:center;gap:10px;align-self:center;padding-right:8px}.action-buttons mat-icon{color:#fff!important;font-size:16px;width:16px;height:16px}.item-action-edit{justify-self:center;text-align:center!important;background:linear-gradient(135deg,var(--av-edit) 0%,#38b6db 100%)!important;height:34px!important;width:34px!important;border-radius:8px!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 6px #4cc9f04d!important;transition:all var(--av-transition)!important}.item-action-edit:hover{transform:translateY(-2px) scale(1.08)!important;box-shadow:0 4px 12px #4cc9f073!important;filter:brightness(1.08)}.item-action-edit:active{transform:translateY(0) scale(.96)!important}.item-action-delete{text-align:center!important;background:linear-gradient(135deg,var(--av-delete) 0%,#d63d62 100%)!important;height:34px!important;width:34px!important;border-radius:8px!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 6px #ef476f4d!important;transition:all var(--av-transition)!important}.item-action-delete:hover{transform:translateY(-2px) scale(1.08)!important;box-shadow:0 4px 12px #ef476f73!important;filter:brightness(1.08)}.item-action-delete:active{transform:translateY(0) scale(.96)!important}.item-action{text-align:center!important;background:linear-gradient(135deg,var(--av-delete) 0%,#d63d62 100%)!important;height:34px!important;width:34px!important;border-radius:8px!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 6px #ef476f4d!important;transition:all var(--av-transition)!important}.item-action:hover{transform:translateY(-2px) scale(1.08)!important;box-shadow:0 4px 12px #ef476f73!important}.paginator-container{margin-top:4px;border-radius:0 0 var(--av-radius-md) var(--av-radius-md);overflow:hidden}.paginator-container mat-paginator{background:var(--av-bg-muted)!important;border-top:1px solid var(--av-border)!important}.filter-menu-panel{min-width:240px!important}.filter-container{padding:12px 14px;display:flex;flex-direction:column;gap:6px}.filter-rule{display:flex;flex-direction:column;gap:4px;padding-bottom:8px;border-bottom:1px dashed var(--av-border)}.filter-rule:last-of-type{border-bottom:none;padding-bottom:0}.full-width{width:100%}.compact ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.compact ::ng-deep .mat-mdc-text-field-wrapper{padding:0 10px!important}.compact ::ng-deep .mat-mdc-form-field-infix{min-height:36px!important;padding-top:6px!important;padding-bottom:6px!important}.compact ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,.compact ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch,.compact ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-color:var(--av-border)!important}.compact ::ng-deep .mat-mdc-form-field-flex{align-items:center}.compact ::ng-deep .mat-mdc-select-trigger{font-size:13px}.compact ::ng-deep input.mat-mdc-input-element{font-size:13px}.compact ::ng-deep .mat-mdc-floating-label{font-size:13px}::ng-deep .global-search-field input.mat-mdc-input-element{font-size:13px}.add-rule-btn{font-size:12px!important;font-weight:600!important;padding:0!important;height:28px!important;color:var(--av-primary)!important;border-radius:var(--av-radius-sm)!important;transition:all var(--av-transition)!important}.add-rule-btn:hover{background:var(--av-primary-light)!important}.add-rule-btn mat-icon{font-size:14px;width:14px;height:14px}.filter-actions{display:flex;align-items:center;margin-top:6px;gap:8px}.filter-actions button{border-radius:var(--av-radius-sm)!important;font-weight:600!important;font-size:12px!important;letter-spacing:.3px;height:32px!important;line-height:32px!important}.no-data-msg{padding:48px 24px;text-align:center;color:var(--av-text-muted);font-size:14px;font-weight:500;font-style:normal;background:var(--av-bg-muted);border-radius:0 0 var(--av-radius-md) var(--av-radius-md);border:1px dashed var(--av-border);border-top:none;margin-top:0;letter-spacing:.2px}mat-form-field{--mat-form-field-container-height: 40px !important;--mat-form-field-container-vertical-padding: 8px !important}\n"], dependencies: [{ kind: "component", type: MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: MatCardContent, selector: "mat-card-content" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i7.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
407
612
  }
408
613
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AvDataTable, decorators: [{
409
614
  type: Component,
@@ -418,10 +623,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImpor
418
623
  MatCardContent,
419
624
  MatIcon,
420
625
  MatIconButton,
421
- NgClass,
422
- NgStyle,
626
+ CommonModule,
423
627
  MatPaginator,
424
- ], standalone: true, template: "@if (TableData.length > 0) {\r\n <div class=\"item-header-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (header of getDisplayedHeaders(); track $index) {\r\n <strong [ngClass]=\"getHeaderFieldClasses(header)\">\r\n {{ header.label }}\r\n </strong>\r\n }\r\n </div>\r\n\r\n <!-- Display only the paginated items -->\r\n @for (item of getPaginatedData(); track $index) {\r\n <mat-card>\r\n <mat-card-content>\r\n <div class=\"item-details\">\r\n <div class=\"item-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (column of TableColumns; track column) {\r\n <span [ngClass]=\"getDataFieldClasses(column)\"\r\n [ngStyle]=\"{ color: getDataFiledColor(column) }\"\r\n >{{ getValueForColumn(item, column) }}</span>\r\n }\r\n @if (EnableActionColumn) {\r\n <div class=\"action-buttons\">\r\n @if (EnableButtonModify) {\r\n <button\r\n class=\"item-action-edit\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"modifyItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">edit</mat-icon>\r\n </button>\r\n }\r\n @if (EnableButtonDelete) {\r\n <button\r\n class=\"item-action-delete\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"removeItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n </mat-card>\r\n }\r\n\r\n <!-- Move paginator outside the loop -->\r\n <div class=\"paginator-container\">\r\n <mat-paginator\r\n [pageSize]=\"PageSize\"\r\n [length]=\"TableData.length\"\r\n [pageSizeOptions]=\"PageSizeOptions\"\r\n [pageIndex]=\"currentPage\"\r\n (page)=\"pageChange($event)\"\r\n showFirstLastButtons>\r\n </mat-paginator>\r\n </div>\r\n}\r\n", styles: [".mat-header-cell{font-weight:700}mat-card{width:100%;background:none;border:unset;box-shadow:unset;margin:.2rem}mat-card-header{border-top-left-radius:10px;border-top-right-radius:10px;padding-bottom:15px;border-bottom:black solid 1px}mat-card-content{border-radius:unset!important;padding-top:15px;background:unset;overflow:hidden;height:100%}button:disabled{opacity:.5;cursor:not-allowed!important}mat-icon{color:#fff!important;font-size:18px;width:18px;height:18px}.item-header-row{display:grid;padding:12px 20px;background:#f5f7fa;border-bottom:2px solid #e0e4e8;border-top:2px solid #e0e4e8;margin-top:15px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;letter-spacing:.3px}.item-header-row strong{font-weight:600;color:#4a5568;font-size:14px;text-transform:uppercase}.header-text-left{text-align:left;padding-left:16px!important}.header-text-center{text-align:center}.header-text-right{text-align:right}.text-left{text-align:left;padding-left:5px!important;font-weight:500}.text-center{text-align:center;font-weight:500}.text-right{text-align:right}.item-details{width:100%;margin-bottom:-13px!important;background-color:#fff;box-shadow:0 2px 8px #0000000d;overflow:hidden;transition:all .2s ease}.item-row{width:100%;display:grid;align-items:center;padding:10px}.action-buttons{display:flex;flex-direction:row;align-items:center;justify-content:center;margin-left:25px;align-self:center;gap:5px}.item-action-delete{text-align:right!important;margin-right:60px!important;background-color:#f56565!important;height:36px!important;width:36px!important;border-radius:50%!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 4px #f5656533!important}.item-action-edit{justify-self:center;text-align:right!important;background-color:#65a0f6!important;height:36px!important;width:36px!important;border-radius:50%!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 4px #f5656533!important}.item-action-edit mat-icon{color:#fff!important}.item-action{text-align:right!important;margin-right:60px!important;background-color:#f56565!important;height:36px!important;width:36px!important;border-radius:50%!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 4px #f5656533!important}.paginator-container mat-paginator{background:transparent!important;border-top:1px solid #f1f1f1!important}\n"] }]
628
+ MatMenuModule,
629
+ MatFormFieldModule,
630
+ MatInputModule,
631
+ MatSelectModule,
632
+ MatButtonModule,
633
+ FormsModule
634
+ ], standalone: true, template: "<!-- Global Search and Clear -->\r\n<div class=\"table-top-actions\">\r\n <button mat-stroked-button (click)=\"clearAllFilters()\" class=\"clear-filters-btn\">\r\n <mat-icon>filter_alt_off</mat-icon> Clear\r\n </button>\r\n <div class=\"spacer\"></div>\r\n <mat-form-field appearance=\"outline\" class=\"global-search-field\">\r\n <mat-icon matIconPrefix>search</mat-icon>\r\n <mat-label>Keyboard Search</mat-label>\r\n <input matInput [(ngModel)]=\"globalSearchTerm\" (input)=\"applyAllFilters()\" placeholder=\"Search...\">\r\n </mat-form-field>\r\n</div>\r\n\r\n@if (processedData.length > 0) {\r\n<div class=\"item-header-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (header of getDisplayedHeaders(); track $index) {\r\n <div class=\"header-cell\" [ngClass]=\"getHeaderFieldClasses(header)\">\r\n <div class=\"header-content\" (click)=\"TableColumns[$index] ? toggleSort(TableColumns[$index].field) : null\">\r\n <strong>{{ header.label }}</strong>\r\n @if (TableColumns[$index]) {\r\n <mat-icon class=\"sort-icon\" [ngClass]=\"{'active': sortField === TableColumns[$index].field}\">\r\n @if (sortField !== TableColumns[$index].field || sortDirection === '') {\r\n unfold_more\r\n } @else if (sortDirection === 'asc') {\r\n expand_less\r\n } @else {\r\n expand_more\r\n }\r\n </mat-icon>\r\n }\r\n </div>\r\n\r\n @if (TableColumns[$index]) {\r\n <button mat-icon-button [matMenuTriggerFor]=\"filterMenu\" class=\"filter-btn\">\r\n <mat-icon [class.active-filter]=\"hasActiveFilter(TableColumns[$index].field)\">filter_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #filterMenu=\"matMenu\" class=\"filter-menu-panel\">\r\n <div class=\"filter-container\" (click)=\"$event.stopPropagation()\">\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <mat-select [(ngModel)]=\"getColumnFilter(TableColumns[$index].field).matchMode\">\r\n <mat-option value=\"all\">Match All</mat-option>\r\n <mat-option value=\"any\">Match Any</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n @for (rule of getColumnFilter(TableColumns[$index].field).rules; track rule; let ri = $index) {\r\n <div class=\"filter-rule\">\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <mat-select [(ngModel)]=\"rule.operator\">\r\n <mat-option value=\"starts\">Starts with</mat-option>\r\n <mat-option value=\"contains\">Contains</mat-option>\r\n <mat-option value=\"not_contains\">Not contains</mat-option>\r\n <mat-option value=\"ends\">Ends with</mat-option>\r\n <mat-option value=\"equals\">Equals</mat-option>\r\n <mat-option value=\"not_equals\">Not equals</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <input matInput [(ngModel)]=\"rule.value\" placeholder=\"Value\">\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <button mat-button color=\"primary\" class=\"add-rule-btn\"\r\n (click)=\"getColumnFilter(TableColumns[$index].field).rules.push({operator: 'starts', value: ''})\">\r\n <mat-icon>add</mat-icon> Add Rule\r\n </button>\r\n\r\n <div class=\"filter-actions\">\r\n <button mat-button\r\n (click)=\"getColumnFilter(TableColumns[$index].field).rules = [{operator: 'starts', value: ''}]; setColumnFilter(TableColumns[$index].field, null)\">Clear</button>\r\n <div class=\"spacer\"></div>\r\n <button mat-flat-button color=\"primary\"\r\n (click)=\"setColumnFilter(TableColumns[$index].field, getColumnFilter(TableColumns[$index].field))\">Apply</button>\r\n </div>\r\n </div>\r\n </mat-menu>\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Display only the paginated items -->\r\n@for (item of getPaginatedData(); track $index) {\r\n<mat-card>\r\n <mat-card-content>\r\n <div class=\"item-details\">\r\n <div class=\"item-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (column of TableColumns; track column) {\r\n <span [ngClass]=\"getDataFieldClasses(column)\" [ngStyle]=\"{ color: getDataFiledColor(column) }\">{{\r\n getValueForColumn(item, column) }}</span>\r\n }\r\n @if (EnableActionColumn) {\r\n <div class=\"action-buttons\">\r\n @if (EnableButtonModify) {\r\n <button class=\"item-action-edit\" mat-icon-button color=\"warn\" (click)=\"modifyItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">edit</mat-icon>\r\n </button>\r\n }\r\n @if (EnableButtonDelete) {\r\n <button class=\"item-action-delete\" mat-icon-button color=\"warn\" (click)=\"removeItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n</mat-card>\r\n}\r\n\r\n<!-- Move paginator outside the loop -->\r\n<div class=\"paginator-container\">\r\n <mat-paginator [pageSize]=\"PageSize\" [length]=\"processedData.length\" [pageSizeOptions]=\"PageSizeOptions\"\r\n [pageIndex]=\"currentPage\" (page)=\"pageChange($event)\" showFirstLastButtons>\r\n </mat-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"no-data-msg\">\r\n No items found matching your search/filters.\r\n</div>\r\n}", styles: [":host{--av-primary: #4361ee;--av-primary-light: #eef1ff;--av-primary-hover: #3651d4;--av-accent: #f72585;--av-accent-light: #fee2ef;--av-edit: #4cc9f0;--av-edit-hover: #3ab8df;--av-delete: #ef476f;--av-delete-hover: #d63d62;--av-text-primary: #1e293b;--av-text-secondary: #64748b;--av-text-muted: #94a3b8;--av-bg-surface: #ffffff;--av-bg-muted: #f8fafc;--av-border: #e2e8f0;--av-border-hover: #cbd5e1;--av-shadow-sm: 0 1px 3px rgba(0, 0, 0, .06);--av-shadow-md: 0 4px 12px rgba(0, 0, 0, .08);--av-shadow-lg: 0 8px 24px rgba(0, 0, 0, .1);--av-radius-sm: 6px;--av-radius-md: 10px;--av-radius-lg: 14px;--av-transition: .25s cubic-bezier(.4, 0, .2, 1);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}mat-card{width:100%;background:none;border:unset;box-shadow:unset;margin:0;padding:0}mat-card-content{border-radius:unset!important;padding:0!important;background:unset;overflow:hidden}mat-icon{font-size:18px;width:18px;height:18px}button:disabled{opacity:.4;cursor:not-allowed!important;filter:grayscale(.3)}.table-top-actions{display:flex;align-items:center;padding:12px 4px;gap:12px}.spacer{flex:1}.clear-filters-btn{border-radius:var(--av-radius-sm)!important;color:var(--av-text-secondary)!important;border-color:var(--av-border)!important;font-weight:500!important;font-size:13px!important;letter-spacing:.3px;transition:all var(--av-transition)!important;padding:0 14px!important;height:38px!important}.clear-filters-btn:hover{background:var(--av-accent-light)!important;color:var(--av-accent)!important;border-color:var(--av-accent)!important;box-shadow:0 2px 8px #f725851f}.clear-filters-btn mat-icon{margin-right:5px;font-size:17px;width:17px;height:17px;transition:color var(--av-transition)}.clear-filters-btn:hover mat-icon{color:var(--av-accent)!important}.global-search-field mat-icon{color:var(--av-text-muted);transition:color var(--av-transition)}::ng-deep .global-search-field .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .global-search-field .mat-mdc-form-field-icon-prefix mat-icon,::ng-deep .global-search-field .mat-mdc-form-field-prefix mat-icon{font-size:20px;width:20px;color:var(--av-text-muted)}::ng-deep .global-search-field .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,::ng-deep .global-search-field .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch,::ng-deep .global-search-field .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-color:var(--av-border)!important}::ng-deep .global-search-field.mat-focused .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,::ng-deep .global-search-field.mat-focused .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch,::ng-deep .global-search-field.mat-focused .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-color:var(--av-primary)!important}.item-header-row{display:grid;padding:0 20px;background:linear-gradient(135deg,#f1f5f9,#e8edf3);border-bottom:2px solid var(--av-primary);border-radius:var(--av-radius-md) var(--av-radius-md) 0 0;margin-top:8px;font-family:inherit;letter-spacing:.4px;min-height:48px;align-items:center}.item-header-row strong{font-weight:700;color:var(--av-text-primary);font-size:12px;text-transform:uppercase;letter-spacing:.8px}.header-text-left{text-align:left;padding-left:16px!important}.header-text-center{text-align:center}.header-text-right{text-align:right}.text-left{text-align:left;padding-left:5px!important;font-weight:500}.text-center{text-align:center;font-weight:500}.text-right{text-align:right}.header-cell{display:flex!important;align-items:center;justify-content:inherit;gap:2px}.header-content{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;padding:4px 6px;border-radius:var(--av-radius-sm);transition:background var(--av-transition)}.header-content:hover{background:#4361ee14}.header-content:active{background:#4361ee26}.sort-icon{font-size:16px;width:16px;height:16px;margin-left:4px;color:var(--av-text-muted);transition:all var(--av-transition);opacity:.5}.header-content:hover .sort-icon{opacity:1;color:var(--av-primary)}.sort-icon.active{color:var(--av-primary)!important;opacity:1!important;animation:sortBounce .3s ease}@keyframes sortBounce{0%{transform:scale(1)}50%{transform:scale(1.3)}to{transform:scale(1)}}.filter-btn{width:30px!important;height:30px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important;border-radius:var(--av-radius-sm)!important;transition:all var(--av-transition)!important}.filter-btn:hover{background:#4361ee14!important}.filter-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--av-text-muted);transition:all var(--av-transition)}.filter-btn:hover mat-icon{color:var(--av-primary)}.active-filter{color:var(--av-primary)!important;position:relative}.item-details{width:100%;margin-bottom:0!important;background-color:var(--av-bg-surface);border-left:3px solid transparent;border-bottom:1px solid var(--av-border);transition:all var(--av-transition);cursor:default}.item-details:hover{background:linear-gradient(90deg,var(--av-primary-light) 0%,var(--av-bg-surface) 40%);border-left-color:var(--av-primary);box-shadow:var(--av-shadow-sm)}.item-row{width:100%;display:grid;align-items:center;padding:14px 24px;font-size:14px;color:var(--av-text-primary);line-height:1.5;overflow:visible}.item-row span{transition:color var(--av-transition)}.action-buttons{display:flex;flex-direction:row;align-items:center;justify-content:center;gap:10px;align-self:center;padding-right:8px}.action-buttons mat-icon{color:#fff!important;font-size:16px;width:16px;height:16px}.item-action-edit{justify-self:center;text-align:center!important;background:linear-gradient(135deg,var(--av-edit) 0%,#38b6db 100%)!important;height:34px!important;width:34px!important;border-radius:8px!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 6px #4cc9f04d!important;transition:all var(--av-transition)!important}.item-action-edit:hover{transform:translateY(-2px) scale(1.08)!important;box-shadow:0 4px 12px #4cc9f073!important;filter:brightness(1.08)}.item-action-edit:active{transform:translateY(0) scale(.96)!important}.item-action-delete{text-align:center!important;background:linear-gradient(135deg,var(--av-delete) 0%,#d63d62 100%)!important;height:34px!important;width:34px!important;border-radius:8px!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 6px #ef476f4d!important;transition:all var(--av-transition)!important}.item-action-delete:hover{transform:translateY(-2px) scale(1.08)!important;box-shadow:0 4px 12px #ef476f73!important;filter:brightness(1.08)}.item-action-delete:active{transform:translateY(0) scale(.96)!important}.item-action{text-align:center!important;background:linear-gradient(135deg,var(--av-delete) 0%,#d63d62 100%)!important;height:34px!important;width:34px!important;border-radius:8px!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:0 2px 6px #ef476f4d!important;transition:all var(--av-transition)!important}.item-action:hover{transform:translateY(-2px) scale(1.08)!important;box-shadow:0 4px 12px #ef476f73!important}.paginator-container{margin-top:4px;border-radius:0 0 var(--av-radius-md) var(--av-radius-md);overflow:hidden}.paginator-container mat-paginator{background:var(--av-bg-muted)!important;border-top:1px solid var(--av-border)!important}.filter-menu-panel{min-width:240px!important}.filter-container{padding:12px 14px;display:flex;flex-direction:column;gap:6px}.filter-rule{display:flex;flex-direction:column;gap:4px;padding-bottom:8px;border-bottom:1px dashed var(--av-border)}.filter-rule:last-of-type{border-bottom:none;padding-bottom:0}.full-width{width:100%}.compact ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.compact ::ng-deep .mat-mdc-text-field-wrapper{padding:0 10px!important}.compact ::ng-deep .mat-mdc-form-field-infix{min-height:36px!important;padding-top:6px!important;padding-bottom:6px!important}.compact ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,.compact ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch,.compact ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-color:var(--av-border)!important}.compact ::ng-deep .mat-mdc-form-field-flex{align-items:center}.compact ::ng-deep .mat-mdc-select-trigger{font-size:13px}.compact ::ng-deep input.mat-mdc-input-element{font-size:13px}.compact ::ng-deep .mat-mdc-floating-label{font-size:13px}::ng-deep .global-search-field input.mat-mdc-input-element{font-size:13px}.add-rule-btn{font-size:12px!important;font-weight:600!important;padding:0!important;height:28px!important;color:var(--av-primary)!important;border-radius:var(--av-radius-sm)!important;transition:all var(--av-transition)!important}.add-rule-btn:hover{background:var(--av-primary-light)!important}.add-rule-btn mat-icon{font-size:14px;width:14px;height:14px}.filter-actions{display:flex;align-items:center;margin-top:6px;gap:8px}.filter-actions button{border-radius:var(--av-radius-sm)!important;font-weight:600!important;font-size:12px!important;letter-spacing:.3px;height:32px!important;line-height:32px!important}.no-data-msg{padding:48px 24px;text-align:center;color:var(--av-text-muted);font-size:14px;font-weight:500;font-style:normal;background:var(--av-bg-muted);border-radius:0 0 var(--av-radius-md) var(--av-radius-md);border:1px dashed var(--av-border);border-top:none;margin-top:0;letter-spacing:.2px}mat-form-field{--mat-form-field-container-height: 40px !important;--mat-form-field-container-vertical-padding: 8px !important}\n"] }]
425
635
  }], ctorParameters: () => [], propDecorators: { PageSize: [{
426
636
  type: Input
427
637
  }], PageSizeOptions: [{
@@ -1 +1 @@
1
- {"version":3,"file":"avoraui-av-data-table.mjs","sources":["../../../projects/av-data-table/src/lib/av-data-table.ts","../../../projects/av-data-table/src/lib/av-data-table.html","../../../projects/av-data-table/src/public-api.ts","../../../projects/av-data-table/src/avoraui-av-data-table.ts"],"sourcesContent":["import {Component, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core';\r\nimport {MatCard, MatCardContent} from \"@angular/material/card\";\r\nimport {MatIcon} from \"@angular/material/icon\";\r\nimport {MatIconButton} from \"@angular/material/button\";\r\nimport {NgClass, NgStyle} from \"@angular/common\";\r\nimport {ControlValueAccessor, NG_VALUE_ACCESSOR} from \"@angular/forms\";\r\nimport {MatPaginator, PageEvent} from \"@angular/material/paginator\";\r\nimport {Headers, Columns} from './table-prop';\r\n\r\n@Component({\r\n selector: 'av-data-table',\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => AvDataTable),\r\n multi: true\r\n }\r\n ],\r\n imports: [\r\n MatCard,\r\n MatCardContent,\r\n MatIcon,\r\n MatIconButton,\r\n NgClass,\r\n NgStyle,\r\n MatPaginator,\r\n ],\r\n templateUrl: './av-data-table.html',\r\n styleUrl: './av-data-table.css',\r\n standalone: true\r\n})\r\nexport class AvDataTable implements OnInit, OnChanges, ControlValueAccessor {\r\n\r\n @Input() PageSize: number = 5;\r\n @Input() PageSizeOptions: Array<number> = [];\r\n @Input() currentPage: number = 0;\r\n @Input() TableHeaders: Headers[] = [];\r\n @Input() TableColumns: Columns[] = [];\r\n protected TableData: any[] = [];\r\n @Input() Data: any[] = [];\r\n @Input() EnableActionColumn: boolean = false;\r\n @Input() EnableButtonDelete: boolean = false;\r\n @Input() EnableButtonModify: boolean = false;\r\n @Output() onModify = new EventEmitter<{ index: number; modifiedItem: any, disabled: boolean }>();\r\n @Output() onNewItemAdded = new EventEmitter<{index: number, dataSize: number, removedItem: any}>();\r\n @Output() onItemRemoved = new EventEmitter<{index: number, dataSize: number, removedItem: any, disabled: boolean}>();\r\n @Input() DisableRemove: boolean = false;\r\n @Input() DisableModify: boolean = false;\r\n gridTemplateColumns: string = '';\r\n private onChange: (value: any) => void = () => {};\r\n private onTouched: () => void = () => {};\r\n\r\n constructor(\r\n ) {\r\n }\r\n\r\n /**\r\n * Lifecycle hook that is called after Angular has initialized all data-bound properties of a directive.\r\n * This method is used to perform initialization logic for the component.\r\n *\r\n * It executes the following:\r\n * - Initializes the paginator for managing paginated data display.\r\n * - Calculates the grid template for layout adjustments.\r\n * - Synchronizes data sources to ensure the component has up-to-date data.\r\n *\r\n * @return {void} This method does not return any value.\r\n */\r\n ngOnInit(): void {\r\n this.initializePaginator();\r\n this.calculateGridTemplate();\r\n this.syncDataSources();\r\n }\r\n\r\n /**\r\n * Initializes the paginator configuration. Sets default values for PageSize, PageSizeOptions,\r\n * and currentPage if they are undefined, null, or invalid. Ensures the PageSize is included\r\n * in the PageSizeOptions array and sorts the options in ascending order.\r\n *\r\n * @return {void} Does not return a value.\r\n */\r\n initializePaginator(): void {\r\n if (this.PageSize === undefined || this.PageSize === null || this.PageSize <= 0) {\r\n this.PageSize = 5;\r\n }\r\n if (this.PageSizeOptions === undefined || this.PageSizeOptions === null || this.PageSizeOptions.length === 0) {\r\n this.PageSizeOptions = [5, 10, 20, 50];\r\n }\r\n if (this.currentPage === undefined || this.currentPage === null || this.currentPage < 0) {\r\n this.currentPage = 0;\r\n }\r\n\r\n // Ensure PageSize is included in PageSizeOptions if not already present\r\n if (!this.PageSizeOptions.includes(this.PageSize)) {\r\n this.PageSizeOptions = [...this.PageSizeOptions, this.PageSize].sort((a, b) => a - b);\r\n }\r\n }\r\n\r\n /**\r\n * Handles changes to the component's input properties during the lifecycle of the component.\r\n *\r\n * This method is triggered whenever an input property bound to the component changes. It processes\r\n * updates to properties such as `Data`, `PageSize`, `PageSizeOptions`, and `currentPage`, and\r\n * ensures the internal state is kept in sync with the new inputs. Additionally, it updates the paginator\r\n * and adjusts the current page as necessary.\r\n *\r\n * @param {SimpleChanges} changes - A collection of SimpleChange objects representing the changed properties.\r\n * Each `SimpleChange` object provides information like the current and previous values as well as a flag\r\n * indicating if it is the first change to this input.\r\n * @return {void} - This method does not return any value.\r\n */\r\n ngOnChanges(changes: SimpleChanges): void {\r\n // Listen for changes in the external Data input\r\n if (changes['Data'] && !changes['Data'].firstChange) {\r\n this.syncDataSources();\r\n // Reset to first page when data changes\r\n this.currentPage = 0;\r\n }\r\n\r\n // Handle changes in pagination settings\r\n if (changes['PageSize'] || changes['PageSizeOptions'] || changes['currentPage']) {\r\n this.initializePaginator();\r\n\r\n // Validate current page doesn't exceed available pages\r\n const totalPages = Math.ceil(this.TableData.length / this.PageSize);\r\n if (this.currentPage >= totalPages && totalPages > 0) {\r\n this.currentPage = totalPages - 1;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves a subset of data from the full dataset based on the current page and page size.\r\n *\r\n * @return {Array} A portion of the TableData array corresponding to the current page.\r\n */\r\n getPaginatedData(): Array<any> {\r\n const startIndex = this.currentPage * this.PageSize;\r\n const endIndex = startIndex + this.PageSize;\r\n return this.TableData.slice(startIndex, endIndex);\r\n }\r\n\r\n /**\r\n * Handles changes in the pagination state, such as changing the current page or the page size.\r\n *\r\n * @param {PageEvent} event - The event triggered by a pagination action that contains the updated page index and page size.\r\n * @return {void} This method does not return anything.\r\n */\r\n pageChange(event: PageEvent): void {\r\n this.currentPage = event.pageIndex;\r\n this.PageSize = event.pageSize;\r\n }\r\n\r\n /**\r\n * Calculates the actual index in the dataset based on the paginated index.\r\n *\r\n * @param {number} paginatedIndex - The index within the current page of paginated data.\r\n * @return {number} The actual index in the entire dataset.\r\n */\r\n getActualIndex(paginatedIndex: number): number {\r\n return (this.currentPage * this.PageSize) + paginatedIndex;\r\n }\r\n\r\n /**\r\n * Synchronizes external data source with the internal TableData.\r\n * If the external Data differs from the current TableData, the TableData\r\n * is updated with the values from Data and a change notification is triggered.\r\n *\r\n * @return {void} This method does not return a value.\r\n */\r\n syncDataSources(): void {\r\n // If external Data is provided and differs from TableData, update TableData\r\n if (this.Data && this.Data.length > 0) {\r\n // Check if Data and TableData are different\r\n const dataString = JSON.stringify(this.Data);\r\n const tableDataString = JSON.stringify(this.TableData);\r\n\r\n if (dataString !== tableDataString) {\r\n this.TableData = [...this.Data];\r\n // console.log(\"Changed Data : \" + this.Data);\r\n // Notify the form about the change\r\n this.onChange(this.TableData);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Updates the form value by notifying changes and updating external data if available.\r\n * This method triggers the onChange callback with the current table data\r\n * and notifies that the form field has been touched. Additionally, it updates\r\n * the external data reference to ensure proper change detection in the parent components.\r\n *\r\n * @return {void} No return value.\r\n */\r\n updateFormValue(): void {\r\n // Notify the form about the change\r\n if (this.onChange) {\r\n this.onChange(this.TableData);\r\n this.onTouched();\r\n }\r\n\r\n // Also update the external Data if it exists\r\n if (this.Data) {\r\n // Create a new reference to trigger OnChanges in parent components\r\n this.Data = [...this.TableData];\r\n }\r\n }\r\n\r\n /**\r\n * Calculates and sets the grid template for a layout based on the number of displayed headers\r\n * and the presence of an action column. The grid template determines the column structure with\r\n * equal fractions for data columns and a fixed width for the action column if enabled.\r\n *\r\n * @return {void} Does not return a value. Sets the `gridTemplateColumns` property to the calculated template.\r\n */\r\n calculateGridTemplate(): void {\r\n // Calculate grid template based on columns count\r\n const displayedHeaders = this.getDisplayedHeaders();\r\n const columnsCount = displayedHeaders.length;\r\n\r\n if (columnsCount === 0) return;\r\n\r\n // Create grid template with equal fractions for data columns\r\n // and fixed width for action column if enabled\r\n if (this.EnableActionColumn) {\r\n const dataColumns = columnsCount - 1; // Subtract action column\r\n this.gridTemplateColumns = dataColumns > 0\r\n ? `repeat(${dataColumns}, 1fr) 100px`\r\n : '100px';\r\n } else {\r\n this.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves the headers that should be displayed in the table.\r\n * The method includes or excludes the \"action\" column based on the value of `EnableActionColumn`.\r\n *\r\n * @return {TableProp[]} An array of headers to be displayed in the table. If `EnableActionColumn` is false, the \"action\" column is excluded.\r\n */\r\n getDisplayedHeaders(): Headers[] {\r\n return this.EnableActionColumn\r\n ? this.TableHeaders\r\n : this.TableHeaders.filter(h => h.label.toLowerCase() !== 'action');\r\n }\r\n\r\n /**\r\n * Generates and returns an object representing CSS classes for a header based on its alignment property.\r\n *\r\n * @param {TableProp} header - An object containing header properties, including an alignment property ('center', 'right', or 'left').\r\n * @return {Object} An object mapping class names to a boolean indicating their applicability based on the alignment of the header.\r\n */\r\n getHeaderFieldClasses(header: Headers): object {\r\n return {\r\n 'header-text-center': header.align === 'center',\r\n 'header-text-right': header.align === 'right',\r\n 'header-text-left': header.align === 'left',\r\n };\r\n }\r\n\r\n /**\r\n * Generates a mapping of CSS classes for a data field based on the alignment property of the given column.\r\n *\r\n * @param {Columns} column - The column object containing alignment information.\r\n * @return {Object} An object where the keys are class names, and the values are booleans indicating whether each class applies.\r\n */\r\n getDataFieldClasses(column: Columns): object {\r\n return {\r\n 'text-center': column.align === 'center',\r\n 'text-right': column.align === 'right',\r\n 'text-left': column.align === 'left',\r\n };\r\n }\r\n\r\n /**\r\n * Retrieves the color property from the given column object.\r\n *\r\n * @param {Columns} column - The column object containing the color property.\r\n * @return {string} The color associated with the column.\r\n */\r\n getDataFiledColor(column: Columns): any {\r\n return column.color;\r\n }\r\n\r\n /**\r\n * Retrieves the value of a specified column from the given item.\r\n *\r\n * @param {any} item - The object containing the data to extract the value from.\r\n * @param {Columns} column - The column definition, including the field name or path.\r\n * @return {string} The value of the specified column for the given item. If the field is not found, an empty string is returned.\r\n */\r\n getValueForColumn(item: any, column: Columns): string {\r\n if (!item || !column || !column.field) return '';\r\n\r\n // Check if the field has dot notation (e.g., 'gender.name')\r\n if (column.field.includes('.')) {\r\n return this.getNestedValue(item, column.field);\r\n } else {\r\n // Handle standard fields\r\n return column.field in item ? item[column.field] : '';\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves a nested value from a given object based on a dot-separated path string.\r\n * If the path does not exist or the value is undefined/null, it returns a placeholder '-'.\r\n * If accessing an array, it maps the values at the given key and processes them.\r\n *\r\n * @param {any} object - The object from which the nested value is extracted.\r\n * @param {string} path - The dot-separated string representing the path to the desired value.\r\n * @return {any} The value found at the given path, an array joined into a string, or '-' if not found.\r\n */\r\n getNestedValue(object: any, path: string): any {\r\n // Return early if object is null or undefined\r\n if (!object) return '-';\r\n\r\n // Split the path into individual keys (e.g., \"gender.name\" → [\"gender\", \"name\"])\r\n const keys = path.split('.');\r\n let value = object;\r\n\r\n // Navigate through the object hierarchy\r\n for (const key of keys) {\r\n if (value === null || value === undefined) {\r\n return '-';\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n // If we encounter an array, map over its items to extract the property\r\n value = value.map(item => item[key]).filter(Boolean);\r\n } else {\r\n // Access the next property level\r\n value = value[key];\r\n }\r\n }\r\n\r\n // Format the final result\r\n if (Array.isArray(value)) {\r\n return value.length > 0 ? value.join(', ') : '-';\r\n }\r\n\r\n return value !== null && value !== undefined ? value : '-';\r\n }\r\n\r\n /**\r\n * Removes an item from the data table at the specified index.\r\n *\r\n * @param {number} actualIndex - The index of the item to remove from the data table.\r\n * @return {void} This method does not return a value.\r\n */\r\n removeItem(actualIndex: number): void {\r\n // Check disable condition FIRST - before touching any data\r\n if (this.DisableRemove) {\r\n // Don't remove from TableData, just emit event for parent to handle\r\n this.onItemRemoved.emit({\r\n index: actualIndex,\r\n dataSize: this.TableData.length,\r\n removedItem: this.TableData[actualIndex], // Get the item that would be removed\r\n disabled: true\r\n });\r\n return; // Exit early - don't modify TableData\r\n } else {\r\n // Only proceed with actual removal if not disabled\r\n const newData = [...this.TableData];\r\n const removedData = newData[actualIndex]; // Get the item being removed\r\n newData.splice(actualIndex, 1);\r\n this.TableData = newData;\r\n\r\n // Rest of your existing logic...\r\n const totalPages = Math.ceil(this.TableData.length / this.PageSize);\r\n if (this.currentPage >= totalPages && this.currentPage > 0) {\r\n this.currentPage = totalPages - 1;\r\n }\r\n\r\n if (this.TableData.length === 0) {\r\n this.onChange([removedData]);\r\n } else {\r\n this.updateFormValue();\r\n }\r\n\r\n this.onItemRemoved.emit({\r\n index: actualIndex,\r\n dataSize: this.TableData.length,\r\n removedItem: removedData,\r\n disabled: false\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Modifies an item at the specified index in the dataset. If the modification is disabled, emits an event without making any changes.\r\n *\r\n * @param {number} actualIndex - The index of the item to be modified in the dataset.\r\n * @return {void} This method does not return a value.\r\n */\r\n modifyItem(actualIndex: number): void {\r\n // Check disable condition FIRST - before any modification logic\r\n if (this.DisableModify) {\r\n // Emit event for parent to handle the disabled state\r\n this.onModify.emit({\r\n index: actualIndex,\r\n modifiedItem: this.TableData[actualIndex],\r\n disabled: true\r\n });\r\n return; // Exit early - don't proceed with modification\r\n }\r\n\r\n // Only proceed with modification if not disabled\r\n const modifiedItem = this.TableData[actualIndex];\r\n this.onModify.emit({\r\n index: actualIndex,\r\n modifiedItem,\r\n disabled: false\r\n });\r\n }\r\n\r\n // ControlValueAccessor interface implementation\r\n /**\r\n * Registers a callback function to be called whenever the value changes.\r\n *\r\n * @param {any} fn - The function to be executed on a value change.\r\n * @return {void} This method does not return a value.\r\n */\r\n registerOnChange(fn: any): void {\r\n this.onChange = fn;\r\n }\r\n\r\n /**\r\n * Registers a callback function that should be called when the control is touched.\r\n *\r\n * @param {any} fn - The callback function to execute when the control is touched.\r\n * @return {void} This method does not return a value.\r\n */\r\n registerOnTouched(fn: any): void {\r\n this.onTouched = fn;\r\n }\r\n\r\n /**\r\n * Writes a new value to the table data and resets to the first page.\r\n *\r\n * @param {any[]} value - The array of data to be written to the table.\r\n * @return {void} Does not return a value.\r\n */\r\n writeValue(value: any[]): void {\r\n if (value) {\r\n this.TableData = [...value];\r\n // Reset to first page when new data is written\r\n this.currentPage = 0;\r\n } else {\r\n this.TableData = [];\r\n this.currentPage = 0;\r\n }\r\n }\r\n\r\n}\r\n","@if (TableData.length > 0) {\r\n <div class=\"item-header-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (header of getDisplayedHeaders(); track $index) {\r\n <strong [ngClass]=\"getHeaderFieldClasses(header)\">\r\n {{ header.label }}\r\n </strong>\r\n }\r\n </div>\r\n\r\n <!-- Display only the paginated items -->\r\n @for (item of getPaginatedData(); track $index) {\r\n <mat-card>\r\n <mat-card-content>\r\n <div class=\"item-details\">\r\n <div class=\"item-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (column of TableColumns; track column) {\r\n <span [ngClass]=\"getDataFieldClasses(column)\"\r\n [ngStyle]=\"{ color: getDataFiledColor(column) }\"\r\n >{{ getValueForColumn(item, column) }}</span>\r\n }\r\n @if (EnableActionColumn) {\r\n <div class=\"action-buttons\">\r\n @if (EnableButtonModify) {\r\n <button\r\n class=\"item-action-edit\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"modifyItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">edit</mat-icon>\r\n </button>\r\n }\r\n @if (EnableButtonDelete) {\r\n <button\r\n class=\"item-action-delete\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"removeItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n </mat-card>\r\n }\r\n\r\n <!-- Move paginator outside the loop -->\r\n <div class=\"paginator-container\">\r\n <mat-paginator\r\n [pageSize]=\"PageSize\"\r\n [length]=\"TableData.length\"\r\n [pageSizeOptions]=\"PageSizeOptions\"\r\n [pageIndex]=\"currentPage\"\r\n (page)=\"pageChange($event)\"\r\n showFirstLastButtons>\r\n </mat-paginator>\r\n </div>\r\n}\r\n","/*\r\n * Public API Surface of data-table\r\n */\r\n\r\nexport * from './lib/av-data-table';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"avoraui-av-data-table.mjs","sources":["../../../projects/av-data-table/src/lib/av-data-table.ts","../../../projects/av-data-table/src/lib/av-data-table.html","../../../projects/av-data-table/src/public-api.ts","../../../projects/av-data-table/src/avoraui-av-data-table.ts"],"sourcesContent":["import { Component, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';\r\nimport { MatCard, MatCardContent } from \"@angular/material/card\";\r\nimport { MatIcon } from \"@angular/material/icon\";\r\nimport { MatIconButton } from \"@angular/material/button\";\r\nimport { CommonModule } from \"@angular/common\";\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from \"@angular/forms\";\r\nimport { MatPaginator, PageEvent } from \"@angular/material/paginator\";\r\nimport { MatMenuModule } from \"@angular/material/menu\";\r\nimport { MatFormFieldModule } from \"@angular/material/form-field\";\r\nimport { MatInputModule } from \"@angular/material/input\";\r\nimport { MatSelectModule } from \"@angular/material/select\";\r\nimport { MatButtonModule } from \"@angular/material/button\";\r\nimport { FormsModule } from \"@angular/forms\";\r\nimport { Headers, Columns } from './table-prop';\r\n\r\n/**\r\n * AvDataTable is a standalone Angular component that provides a feature-rich, customizable\r\n * data table interface with advanced pagination, dynamic column rendering, and interactive row actions.\r\n * It offers a flexible and powerful solution for displaying and managing tabular data with support for\r\n * nested object properties, custom styling, and comprehensive CRUD operations.\r\n *\r\n * The component implements ControlValueAccessor to integrate seamlessly with Angular reactive forms,\r\n * supporting bidirectional data binding and form validation. It features server-side and client-side\r\n * pagination, dynamic grid layout calculation, configurable action columns, and event-driven architecture\r\n * for parent component interaction. The table supports nested property access with dot notation, custom\r\n * column alignment, color customization, and intelligent data synchronization.\r\n *\r\n * @template T - The type of items displayed in the table rows.\r\n *\r\n * <p><strong>Features</strong></p>\r\n * <ul>\r\n * <li>Advanced pagination with configurable page sizes and options</li>\r\n * <li>Dynamic grid template generation based on column count</li>\r\n * <li>Nested object property access using dot notation (e.g., 'user.address.city')</li>\r\n * <li>Array value handling with automatic formatting and joining</li>\r\n * <li>Configurable action column with modify and delete operations</li>\r\n * <li>Individual control over action button visibility and disabled states</li>\r\n * <li>Custom column alignment (left, center, right) for headers and data</li>\r\n * <li>Dynamic color customization for table cells</li>\r\n * <li>Event emitters for modify, add, and remove operations</li>\r\n * <li>Automatic page adjustment when data changes or items are removed</li>\r\n * <li>Two-way data synchronization between internal and external data sources</li>\r\n * <li>Angular reactive forms integration via ControlValueAccessor</li>\r\n * <li>Material Design UI components integration</li>\r\n * <li>Change detection optimization with explicit data reference updates</li>\r\n * <li>Graceful handling of null/undefined values with fallback display</li>\r\n * </ul>\r\n *\r\n * <p><strong>Technical Implementation</strong></p>\r\n * <ul>\r\n * <li>Dynamic CSS Grid layout calculation for responsive column sizing</li>\r\n * <li>Paginated data slicing with index transformation for accurate positioning</li>\r\n * <li>Nested property resolution with array mapping and filtering</li>\r\n * <li>Data synchronization using efficient object reference and length comparison</li>\r\n * <li>Event-driven communication pattern with parent components</li>\r\n * <li>Lifecycle management with OnInit and OnChanges implementations</li>\r\n * <li>Conditional action execution based on disabled state flags</li>\r\n * <li>Automatic pagination state management and validation</li>\r\n * </ul>\r\n *\r\n * <p><strong>Event Handling</strong></p>\r\n * <ul>\r\n * <li><b>onModify:</b> Emits when a row modification is requested, includes index, item data, and disabled state</li>\r\n * <li><b>onNewItemAdded:</b> Emits when a new item is added to the table</li>\r\n * <li><b>onItemRemoved:</b> Emits when an item is removed, includes index, data size, removed item, and disabled state</li>\r\n * </ul>\r\n *\r\n * <p><strong>Configuration Options</strong></p>\r\n * <ul>\r\n * <li>PageSize: Number of rows per page (default: 5)</li>\r\n * <li>PageSizeOptions: Array of available page size options</li>\r\n * <li>TableHeaders: Column header definitions with labels and alignment</li>\r\n * <li>TableColumns: Column data definitions with field names, alignment, and colors</li>\r\n * <li>EnableActionColumn: Toggle visibility of action column</li>\r\n * <li>EnableButtonDelete: Control delete button visibility</li>\r\n * <li>EnableButtonModify: Control modify button visibility</li>\r\n * <li>DisableRemove: Prevent item removal while maintaining event emission</li>\r\n * <li>DisableModify: Prevent item modification while maintaining event emission</li>\r\n * </ul>\r\n *\r\n * <p><strong>Authorship</strong></p>\r\n * <ul>\r\n * <li><b>Author:</b> Dileesha Ekanayake</li>\r\n * <li><b>Email:</b> dileesha.r.ekanayake@gmail.com</li>\r\n * <li><b>Created:</b> 2024</li>\r\n * <li><b>Version:</b> 1.0.0</li>\r\n * <li><b>Responsibility:</b> Design, implementation, and documentation of the data table component\r\n * with Angular Material integration. Provides comprehensive table functionality including\r\n * pagination, nested property access, dynamic styling, CRUD operations, and seamless form\r\n * control integration with reactive forms support. Ensures robust data synchronization,\r\n * event-driven architecture, and flexible configuration options for diverse use cases.</li>\r\n * </ul>\r\n */\r\n@Component({\r\n selector: 'av-data-table',\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => AvDataTable),\r\n multi: true\r\n }\r\n ],\r\n imports: [\r\n MatCard,\r\n MatCardContent,\r\n MatIcon,\r\n MatIconButton,\r\n CommonModule,\r\n MatPaginator,\r\n MatMenuModule,\r\n MatFormFieldModule,\r\n MatInputModule,\r\n MatSelectModule,\r\n MatButtonModule,\r\n FormsModule\r\n ],\r\n templateUrl: './av-data-table.html',\r\n styleUrl: './av-data-table.css',\r\n standalone: true\r\n})\r\nexport class AvDataTable implements OnInit, OnChanges, ControlValueAccessor {\r\n\r\n @Input() PageSize: number = 5;\r\n @Input() PageSizeOptions: Array<number> = [];\r\n @Input() currentPage: number = 0;\r\n @Input() TableHeaders: Headers[] = [];\r\n @Input() TableColumns: Columns[] = [];\r\n protected TableData: any[] = [];\r\n @Input() Data: any[] = [];\r\n @Input() EnableActionColumn: boolean = false;\r\n @Input() EnableButtonDelete: boolean = false;\r\n @Input() EnableButtonModify: boolean = false;\r\n @Output() onModify = new EventEmitter<{ index: number; modifiedItem: any, disabled: boolean }>();\r\n @Output() onNewItemAdded = new EventEmitter<{ index: number, dataSize: number, removedItem: any }>();\r\n @Output() onItemRemoved = new EventEmitter<{ index: number, dataSize: number, removedItem: any, disabled: boolean }>();\r\n @Input() DisableRemove: boolean = false;\r\n @Input() DisableModify: boolean = false;\r\n gridTemplateColumns: string = '';\r\n\r\n // New features state\r\n sortField: string = '';\r\n sortDirection: 'asc' | 'desc' | '' = '';\r\n globalSearchTerm: string = '';\r\n columnFilters: Map<string, { matchMode: 'all' | 'any', rules: { operator: string, value: string }[] }> = new Map();\r\n processedData: any[] = [];\r\n\r\n private onChange: (value: any) => void = () => { };\r\n private onTouched: () => void = () => { };\r\n\r\n constructor(\r\n ) {\r\n }\r\n\r\n /**\r\n * Lifecycle hook that is called after Angular has initialized all data-bound properties of a directive.\r\n * This method is used to perform initialization logic for the component.\r\n *\r\n * It executes the following:\r\n * - Initializes the paginator for managing paginated data display.\r\n * - Calculates the grid template for layout adjustments.\r\n * - Synchronizes data sources to ensure the component has up-to-date data.\r\n *\r\n * @return {void} This method does not return any value.\r\n */\r\n ngOnInit(): void {\r\n this.initializePaginator();\r\n this.calculateGridTemplate();\r\n this.syncDataSources();\r\n this.applyAllFilters();\r\n }\r\n\r\n /**\r\n * Initializes the paginator configuration. Sets default values for PageSize, PageSizeOptions,\r\n * and currentPage if they are undefined, null, or invalid. Ensures the PageSize is included\r\n * in the PageSizeOptions array and sorts the options in ascending order.\r\n *\r\n * @return {void} Does not return a value.\r\n */\r\n initializePaginator(): void {\r\n if (this.PageSize === undefined || this.PageSize === null || this.PageSize <= 0) {\r\n this.PageSize = 5;\r\n }\r\n if (this.PageSizeOptions === undefined || this.PageSizeOptions === null || this.PageSizeOptions.length === 0) {\r\n this.PageSizeOptions = [5, 10, 20, 50];\r\n }\r\n if (this.currentPage === undefined || this.currentPage === null || this.currentPage < 0) {\r\n this.currentPage = 0;\r\n }\r\n\r\n // Ensure PageSize is included in PageSizeOptions if not already present\r\n if (!this.PageSizeOptions.includes(this.PageSize)) {\r\n this.PageSizeOptions = [...this.PageSizeOptions, this.PageSize].sort((a, b) => a - b);\r\n }\r\n }\r\n\r\n /**\r\n * Handles changes to the component's input properties during the lifecycle of the component.\r\n *\r\n * This method is triggered whenever an input property bound to the component changes. It processes\r\n * updates to properties such as `Data`, `PageSize`, `PageSizeOptions`, and `currentPage`, and\r\n * ensures the internal state is kept in sync with the new inputs. Additionally, it updates the paginator\r\n * and adjusts the current page as necessary.\r\n *\r\n * @param {SimpleChanges} changes - A collection of SimpleChange objects representing the changed properties.\r\n * Each `SimpleChange` object provides information like the current and previous values as well as a flag\r\n * indicating if it is the first change to this input.\r\n * @return {void} - This method does not return any value.\r\n */\r\n ngOnChanges(changes: SimpleChanges): void {\r\n // Listen for changes in the external Data input\r\n if (changes['Data'] && !changes['Data'].firstChange) {\r\n this.syncDataSources();\r\n this.applyAllFilters();\r\n // Reset to first page when data changes\r\n this.currentPage = 0;\r\n }\r\n\r\n // Handle changes in pagination settings\r\n if (changes['PageSize'] || changes['PageSizeOptions'] || changes['currentPage']) {\r\n this.initializePaginator();\r\n\r\n // Validate current page doesn't exceed available pages\r\n const totalPages = Math.ceil(this.processedData.length / this.PageSize);\r\n if (this.currentPage >= totalPages && totalPages > 0) {\r\n this.currentPage = totalPages - 1;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves a subset of data from the full dataset based on the current page and page size.\r\n *\r\n * @return {Array} A portion of the TableData array corresponding to the current page.\r\n */\r\n getPaginatedData(): Array<any> {\r\n const startIndex = this.currentPage * this.PageSize;\r\n const endIndex = startIndex + this.PageSize;\r\n return this.processedData.slice(startIndex, endIndex);\r\n }\r\n\r\n /**\r\n * Handles changes in the pagination state, such as changing the current page or the page size.\r\n *\r\n * @param {PageEvent} event - The event triggered by a pagination action that contains the updated page index and page size.\r\n * @return {void} This method does not return anything.\r\n */\r\n pageChange(event: PageEvent): void {\r\n this.currentPage = event.pageIndex;\r\n this.PageSize = event.pageSize;\r\n }\r\n\r\n /**\r\n * Calculates the actual index in the dataset based on the paginated index.\r\n *\r\n * @param {number} paginatedIndex - The index within the current page of paginated data.\r\n * @return {number} The actual index in the entire dataset.\r\n */\r\n getActualIndex(paginatedIndex: number): number {\r\n const item = this.getPaginatedData()[paginatedIndex];\r\n return item ? this.TableData.indexOf(item) : -1;\r\n }\r\n\r\n /**\r\n * Synchronizes external data source with the internal TableData.\r\n * If the external Data differs from the current TableData, the TableData\r\n * is updated with the values from Data and a change notification is triggered.\r\n *\r\n * @return {void} This method does not return a value.\r\n */\r\n syncDataSources(): void {\r\n if (this.Data && (this.TableData.length !== this.Data.length || this.TableData !== this.Data)) {\r\n this.TableData = [...this.Data];\r\n this.applyAllFilters();\r\n this.onChange(this.TableData);\r\n }\r\n }\r\n\r\n /**\r\n * Updates the form value by notifying changes and updating external data if available.\r\n * This method triggers the onChange callback with the current table data\r\n * and notifies that the form field has been touched. Additionally, it updates\r\n * the external data reference to ensure proper change detection in the parent components.\r\n *\r\n * @return {void} No return value.\r\n */\r\n updateFormValue(): void {\r\n // Notify the form about the change\r\n if (this.onChange) {\r\n this.onChange(this.TableData);\r\n this.onTouched();\r\n }\r\n\r\n // Also update the external Data if it exists\r\n if (this.Data) {\r\n // Create a new reference to trigger OnChanges in parent components\r\n this.Data = [...this.TableData];\r\n }\r\n }\r\n\r\n /**\r\n * Calculates and sets the grid template for a layout based on the number of displayed headers\r\n * and the presence of an action column. The grid template determines the column structure with\r\n * equal fractions for data columns and a fixed width for the action column if enabled.\r\n *\r\n * @return {void} Does not return a value. Sets the `gridTemplateColumns` property to the calculated template.\r\n */\r\n calculateGridTemplate(): void {\r\n // Calculate grid template based on columns count\r\n const displayedHeaders = this.getDisplayedHeaders();\r\n const columnsCount = displayedHeaders.length;\r\n\r\n if (columnsCount === 0) return;\r\n\r\n // Create grid template with equal fractions for data columns\r\n // and fixed width for action column if enabled\r\n if (this.EnableActionColumn) {\r\n const dataColumns = columnsCount - 1; // Subtract action column\r\n this.gridTemplateColumns = dataColumns > 0\r\n ? `repeat(${dataColumns}, 1fr) 140px`\r\n : '140px';\r\n } else {\r\n this.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves the headers that should be displayed in the table.\r\n * The method includes or excludes the \"action\" column based on the value of `EnableActionColumn`.\r\n *\r\n * @return {TableProp[]} An array of headers to be displayed in the table. If `EnableActionColumn` is false, the \"action\" column is excluded.\r\n */\r\n getDisplayedHeaders(): Headers[] {\r\n return this.EnableActionColumn\r\n ? this.TableHeaders\r\n : this.TableHeaders.filter(h => h.label.toLowerCase() !== 'action');\r\n }\r\n\r\n /**\r\n * Generates and returns an object representing CSS classes for a header based on its alignment property.\r\n *\r\n * @param {TableProp} header - An object containing header properties, including an alignment property ('center', 'right', or 'left').\r\n * @return {Object} An object mapping class names to a boolean indicating their applicability based on the alignment of the header.\r\n */\r\n getHeaderFieldClasses(header: Headers): object {\r\n return {\r\n 'header-text-center': header.align === 'center',\r\n 'header-text-right': header.align === 'right',\r\n 'header-text-left': header.align === 'left',\r\n };\r\n }\r\n\r\n /**\r\n * Generates a mapping of CSS classes for a data field based on the alignment property of the given column.\r\n *\r\n * @param {Columns} column - The column object containing alignment information.\r\n * @return {Object} An object where the keys are class names, and the values are booleans indicating whether each class applies.\r\n */\r\n getDataFieldClasses(column: Columns): object {\r\n return {\r\n 'text-center': column.align === 'center',\r\n 'text-right': column.align === 'right',\r\n 'text-left': column.align === 'left',\r\n };\r\n }\r\n\r\n /**\r\n * Retrieves the color property from the given column object.\r\n *\r\n * @param {Columns} column - The column object containing the color property.\r\n * @return {string} The color associated with the column.\r\n */\r\n getDataFiledColor(column: Columns): any {\r\n return column.color;\r\n }\r\n\r\n /**\r\n * Retrieves the value of a specified column from the given item.\r\n *\r\n * @param {any} item - The object containing the data to extract the value from.\r\n * @param {Columns} column - The column definition, including the field name or path.\r\n * @return {string} The value of the specified column for the given item. If the field is not found, an empty string is returned.\r\n */\r\n getValueForColumn(item: any, column: Columns): string {\r\n if (!item || !column || !column.field) return '';\r\n\r\n // Check if the field has dot notation (e.g., 'gender.name')\r\n if (column.field.includes('.')) {\r\n return this.getNestedValue(item, column.field);\r\n } else {\r\n // Handle standard fields\r\n return column.field in item ? item[column.field] : '';\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves a nested value from a given object based on a dot-separated path string.\r\n * If the path does not exist or the value is undefined/null, it returns a placeholder '-'.\r\n * If accessing an array, it maps the values at the given key and processes them.\r\n *\r\n * @param {any} object - The object from which the nested value is extracted.\r\n * @param {string} path - The dot-separated string representing the path to the desired value.\r\n * @return {any} The value found at the given path, an array joined into a string, or '-' if not found.\r\n */\r\n getNestedValue(object: any, path: string): any {\r\n // Return early if object is null or undefined\r\n if (!object) return '-';\r\n\r\n // Split the path into individual keys (e.g., \"gender.name\" → [\"gender\", \"name\"])\r\n const keys = path.split('.');\r\n let value = object;\r\n\r\n // Navigate through the object hierarchy\r\n for (const key of keys) {\r\n if (value === null || value === undefined) {\r\n return '-';\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n // If we encounter an array, map over its items to extract the property\r\n value = value.map(item => item[key]).filter(Boolean);\r\n } else {\r\n // Access the next property level\r\n value = value[key];\r\n }\r\n }\r\n\r\n // Format the final result\r\n if (Array.isArray(value)) {\r\n return value.length > 0 ? value.join(', ') : '-';\r\n }\r\n\r\n return value !== null && value !== undefined ? value : '-';\r\n }\r\n\r\n /**\r\n * Removes an item from the data table at the specified index.\r\n *\r\n * @param {number} actualIndex - The index of the item to remove from the data table.\r\n * @return {void} This method does not return a value.\r\n */\r\n removeItem(actualIndex: number): void {\r\n // Check disable condition FIRST - before touching any data\r\n if (this.DisableRemove) {\r\n // Don't remove from TableData, just emit event for parent to handle\r\n this.onItemRemoved.emit({\r\n index: actualIndex,\r\n dataSize: this.TableData.length,\r\n removedItem: this.TableData[actualIndex], // Get the item that would be removed\r\n disabled: true\r\n });\r\n return; // Exit early - don't modify TableData\r\n } else {\r\n // Only proceed with actual removal if not disabled\r\n const newData = [...this.TableData];\r\n const removedData = newData[actualIndex]; // Get the item being removed\r\n newData.splice(actualIndex, 1);\r\n this.TableData = newData;\r\n\r\n // Rest of your existing logic...\r\n const totalPages = Math.ceil(this.TableData.length / this.PageSize);\r\n if (this.currentPage >= totalPages && this.currentPage > 0) {\r\n this.currentPage = totalPages - 1;\r\n }\r\n\r\n if (this.TableData.length === 0) {\r\n this.onChange([removedData]);\r\n } else {\r\n this.updateFormValue();\r\n }\r\n\r\n this.applyAllFilters();\r\n\r\n this.onItemRemoved.emit({\r\n index: actualIndex,\r\n dataSize: this.TableData.length,\r\n removedItem: removedData,\r\n disabled: false\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Modifies an item at the specified index in the dataset. If the modification is disabled, emits an event without making any changes.\r\n *\r\n * @param {number} actualIndex - The index of the item to be modified in the dataset.\r\n * @return {void} This method does not return a value.\r\n */\r\n modifyItem(actualIndex: number): void {\r\n // Check disable condition FIRST - before any modification logic\r\n if (this.DisableModify) {\r\n // Emit event for parent to handle the disabled state\r\n this.onModify.emit({\r\n index: actualIndex,\r\n modifiedItem: this.TableData[actualIndex],\r\n disabled: true\r\n });\r\n return; // Exit early - don't proceed with modification\r\n }\r\n\r\n // Only proceed with modification if not disabled\r\n const modifiedItem = this.TableData[actualIndex];\r\n this.onModify.emit({\r\n index: actualIndex,\r\n modifiedItem,\r\n disabled: false\r\n });\r\n }\r\n\r\n // ControlValueAccessor interface implementation\r\n /**\r\n * Registers a callback function to be called whenever the value changes.\r\n *\r\n * @param {any} fn - The function to be executed on a value change.\r\n * @return {void} This method does not return a value.\r\n */\r\n registerOnChange(fn: any): void {\r\n this.onChange = fn;\r\n }\r\n\r\n /**\r\n * Registers a callback function that should be called when the control is touched.\r\n *\r\n * @param {any} fn - The callback function to execute when the control is touched.\r\n * @return {void} This method does not return a value.\r\n */\r\n registerOnTouched(fn: any): void {\r\n this.onTouched = fn;\r\n }\r\n\r\n /**\r\n * Writes a new value to the table data and resets to the first page.\r\n *\r\n * @param {any[]} value - The array of data to be written to the table.\r\n * @return {void} Does not return a value.\r\n */\r\n writeValue(value: any[]): void {\r\n if (value) {\r\n this.TableData = [...value];\r\n this.applyAllFilters();\r\n // Reset to first page when new data is written\r\n this.currentPage = 0;\r\n } else {\r\n this.TableData = [];\r\n this.processedData = [];\r\n this.currentPage = 0;\r\n }\r\n }\r\n\r\n // Sorting Logic\r\n toggleSort(field: string): void {\r\n if (this.sortField === field) {\r\n if (this.sortDirection === 'asc') {\r\n this.sortDirection = 'desc';\r\n } else if (this.sortDirection === 'desc') {\r\n this.sortDirection = '';\r\n this.sortField = '';\r\n } else {\r\n this.sortDirection = 'asc';\r\n }\r\n } else {\r\n this.sortField = field;\r\n this.sortDirection = 'asc';\r\n }\r\n this.applyAllFilters();\r\n }\r\n\r\n // Filtering Logic\r\n applyAllFilters(): void {\r\n let data = [...this.TableData];\r\n\r\n // 1. Global Search\r\n if (this.globalSearchTerm) {\r\n const term = this.globalSearchTerm.toLowerCase();\r\n data = data.filter(item => {\r\n return this.TableColumns.some(col => {\r\n const val = this.getValueForColumn(item, col);\r\n return val?.toString().toLowerCase().includes(term);\r\n });\r\n });\r\n }\r\n\r\n // 2. Column-wise Filters\r\n this.columnFilters.forEach((filter, field) => {\r\n if (filter.rules.length > 0) {\r\n data = data.filter(item => {\r\n const val = (this.getValueForColumn(item, { field, align: 'left' } as any) || '').toString().toLowerCase();\r\n const ruleResults = filter.rules.map(rule => {\r\n const ruleVal = rule.value.toLowerCase();\r\n switch (rule.operator) {\r\n case 'starts': return val.startsWith(ruleVal);\r\n case 'contains': return val.includes(ruleVal);\r\n case 'not_contains': return !val.includes(ruleVal);\r\n case 'ends': return val.endsWith(ruleVal);\r\n case 'equals': return val === ruleVal;\r\n case 'not_equals': return val !== ruleVal;\r\n default: return true;\r\n }\r\n });\r\n\r\n return filter.matchMode === 'all'\r\n ? ruleResults.every(r => r)\r\n : ruleResults.some(r => r);\r\n });\r\n }\r\n });\r\n\r\n // 3. Sorting\r\n if (this.sortField && this.sortDirection) {\r\n data.sort((a, b) => {\r\n const valA = this.getValueForColumn(a, { field: this.sortField, align: 'left' } as any);\r\n const valB = this.getValueForColumn(b, { field: this.sortField, align: 'left' } as any);\r\n\r\n if (valA < valB) return this.sortDirection === 'asc' ? -1 : 1;\r\n if (valA > valB) return this.sortDirection === 'asc' ? 1 : -1;\r\n return 0;\r\n });\r\n }\r\n\r\n this.processedData = data;\r\n\r\n // Adjust pagination if current page is out of bounds\r\n const totalPages = Math.ceil(this.processedData.length / this.PageSize);\r\n if (this.currentPage >= totalPages && totalPages > 0) {\r\n this.currentPage = totalPages - 1;\r\n }\r\n }\r\n\r\n clearAllFilters(): void {\r\n this.globalSearchTerm = '';\r\n this.columnFilters.clear();\r\n this.sortField = '';\r\n this.sortDirection = '';\r\n this.applyAllFilters();\r\n }\r\n\r\n updateGlobalSearch(event: any): void {\r\n this.globalSearchTerm = event.target.value;\r\n this.applyAllFilters();\r\n }\r\n\r\n // Column filter management\r\n setColumnFilter(field: string, filter: any): void {\r\n if (!filter || filter.rules.length === 0) {\r\n this.columnFilters.delete(field);\r\n } else {\r\n this.columnFilters.set(field, filter);\r\n }\r\n this.applyAllFilters();\r\n }\r\n\r\n getColumnFilter(field: string): { matchMode: 'all' | 'any', rules: { operator: string, value: string }[] } {\r\n let filter = this.columnFilters.get(field);\r\n if (!filter) {\r\n filter = { matchMode: 'all', rules: [{ operator: 'starts', value: '' }] };\r\n this.columnFilters.set(field, filter);\r\n }\r\n return filter;\r\n }\r\n\r\n // Check if a column has an active filter with actual values (stable during change detection)\r\n hasActiveFilter(field: string): boolean {\r\n const filter = this.columnFilters.get(field);\r\n if (!filter) return false;\r\n return filter.rules.some(rule => rule.value !== '');\r\n }\r\n}\r\n","<!-- Global Search and Clear -->\r\n<div class=\"table-top-actions\">\r\n <button mat-stroked-button (click)=\"clearAllFilters()\" class=\"clear-filters-btn\">\r\n <mat-icon>filter_alt_off</mat-icon> Clear\r\n </button>\r\n <div class=\"spacer\"></div>\r\n <mat-form-field appearance=\"outline\" class=\"global-search-field\">\r\n <mat-icon matIconPrefix>search</mat-icon>\r\n <mat-label>Keyboard Search</mat-label>\r\n <input matInput [(ngModel)]=\"globalSearchTerm\" (input)=\"applyAllFilters()\" placeholder=\"Search...\">\r\n </mat-form-field>\r\n</div>\r\n\r\n@if (processedData.length > 0) {\r\n<div class=\"item-header-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (header of getDisplayedHeaders(); track $index) {\r\n <div class=\"header-cell\" [ngClass]=\"getHeaderFieldClasses(header)\">\r\n <div class=\"header-content\" (click)=\"TableColumns[$index] ? toggleSort(TableColumns[$index].field) : null\">\r\n <strong>{{ header.label }}</strong>\r\n @if (TableColumns[$index]) {\r\n <mat-icon class=\"sort-icon\" [ngClass]=\"{'active': sortField === TableColumns[$index].field}\">\r\n @if (sortField !== TableColumns[$index].field || sortDirection === '') {\r\n unfold_more\r\n } @else if (sortDirection === 'asc') {\r\n expand_less\r\n } @else {\r\n expand_more\r\n }\r\n </mat-icon>\r\n }\r\n </div>\r\n\r\n @if (TableColumns[$index]) {\r\n <button mat-icon-button [matMenuTriggerFor]=\"filterMenu\" class=\"filter-btn\">\r\n <mat-icon [class.active-filter]=\"hasActiveFilter(TableColumns[$index].field)\">filter_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #filterMenu=\"matMenu\" class=\"filter-menu-panel\">\r\n <div class=\"filter-container\" (click)=\"$event.stopPropagation()\">\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <mat-select [(ngModel)]=\"getColumnFilter(TableColumns[$index].field).matchMode\">\r\n <mat-option value=\"all\">Match All</mat-option>\r\n <mat-option value=\"any\">Match Any</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n @for (rule of getColumnFilter(TableColumns[$index].field).rules; track rule; let ri = $index) {\r\n <div class=\"filter-rule\">\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <mat-select [(ngModel)]=\"rule.operator\">\r\n <mat-option value=\"starts\">Starts with</mat-option>\r\n <mat-option value=\"contains\">Contains</mat-option>\r\n <mat-option value=\"not_contains\">Not contains</mat-option>\r\n <mat-option value=\"ends\">Ends with</mat-option>\r\n <mat-option value=\"equals\">Equals</mat-option>\r\n <mat-option value=\"not_equals\">Not equals</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" class=\"full-width compact\">\r\n <input matInput [(ngModel)]=\"rule.value\" placeholder=\"Value\">\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <button mat-button color=\"primary\" class=\"add-rule-btn\"\r\n (click)=\"getColumnFilter(TableColumns[$index].field).rules.push({operator: 'starts', value: ''})\">\r\n <mat-icon>add</mat-icon> Add Rule\r\n </button>\r\n\r\n <div class=\"filter-actions\">\r\n <button mat-button\r\n (click)=\"getColumnFilter(TableColumns[$index].field).rules = [{operator: 'starts', value: ''}]; setColumnFilter(TableColumns[$index].field, null)\">Clear</button>\r\n <div class=\"spacer\"></div>\r\n <button mat-flat-button color=\"primary\"\r\n (click)=\"setColumnFilter(TableColumns[$index].field, getColumnFilter(TableColumns[$index].field))\">Apply</button>\r\n </div>\r\n </div>\r\n </mat-menu>\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Display only the paginated items -->\r\n@for (item of getPaginatedData(); track $index) {\r\n<mat-card>\r\n <mat-card-content>\r\n <div class=\"item-details\">\r\n <div class=\"item-row\" [ngStyle]=\"{'grid-template-columns': gridTemplateColumns}\">\r\n @for (column of TableColumns; track column) {\r\n <span [ngClass]=\"getDataFieldClasses(column)\" [ngStyle]=\"{ color: getDataFiledColor(column) }\">{{\r\n getValueForColumn(item, column) }}</span>\r\n }\r\n @if (EnableActionColumn) {\r\n <div class=\"action-buttons\">\r\n @if (EnableButtonModify) {\r\n <button class=\"item-action-edit\" mat-icon-button color=\"warn\" (click)=\"modifyItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">edit</mat-icon>\r\n </button>\r\n }\r\n @if (EnableButtonDelete) {\r\n <button class=\"item-action-delete\" mat-icon-button color=\"warn\" (click)=\"removeItem(getActualIndex($index))\">\r\n <mat-icon style=\"color:#ffffff;\">delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n</mat-card>\r\n}\r\n\r\n<!-- Move paginator outside the loop -->\r\n<div class=\"paginator-container\">\r\n <mat-paginator [pageSize]=\"PageSize\" [length]=\"processedData.length\" [pageSizeOptions]=\"PageSizeOptions\"\r\n [pageIndex]=\"currentPage\" (page)=\"pageChange($event)\" showFirstLastButtons>\r\n </mat-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"no-data-msg\">\r\n No items found matching your search/filters.\r\n</div>\r\n}","/*\r\n * Public API Surface of data-table\r\n */\r\n\r\nexport * from './lib/av-data-table';\r\nexport * from './lib/table-prop';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/index.d.ts CHANGED
@@ -13,6 +13,84 @@ interface Columns {
13
13
  color?: string;
14
14
  }
15
15
 
16
+ /**
17
+ * AvDataTable is a standalone Angular component that provides a feature-rich, customizable
18
+ * data table interface with advanced pagination, dynamic column rendering, and interactive row actions.
19
+ * It offers a flexible and powerful solution for displaying and managing tabular data with support for
20
+ * nested object properties, custom styling, and comprehensive CRUD operations.
21
+ *
22
+ * The component implements ControlValueAccessor to integrate seamlessly with Angular reactive forms,
23
+ * supporting bidirectional data binding and form validation. It features server-side and client-side
24
+ * pagination, dynamic grid layout calculation, configurable action columns, and event-driven architecture
25
+ * for parent component interaction. The table supports nested property access with dot notation, custom
26
+ * column alignment, color customization, and intelligent data synchronization.
27
+ *
28
+ * @template T - The type of items displayed in the table rows.
29
+ *
30
+ * <p><strong>Features</strong></p>
31
+ * <ul>
32
+ * <li>Advanced pagination with configurable page sizes and options</li>
33
+ * <li>Dynamic grid template generation based on column count</li>
34
+ * <li>Nested object property access using dot notation (e.g., 'user.address.city')</li>
35
+ * <li>Array value handling with automatic formatting and joining</li>
36
+ * <li>Configurable action column with modify and delete operations</li>
37
+ * <li>Individual control over action button visibility and disabled states</li>
38
+ * <li>Custom column alignment (left, center, right) for headers and data</li>
39
+ * <li>Dynamic color customization for table cells</li>
40
+ * <li>Event emitters for modify, add, and remove operations</li>
41
+ * <li>Automatic page adjustment when data changes or items are removed</li>
42
+ * <li>Two-way data synchronization between internal and external data sources</li>
43
+ * <li>Angular reactive forms integration via ControlValueAccessor</li>
44
+ * <li>Material Design UI components integration</li>
45
+ * <li>Change detection optimization with explicit data reference updates</li>
46
+ * <li>Graceful handling of null/undefined values with fallback display</li>
47
+ * </ul>
48
+ *
49
+ * <p><strong>Technical Implementation</strong></p>
50
+ * <ul>
51
+ * <li>Dynamic CSS Grid layout calculation for responsive column sizing</li>
52
+ * <li>Paginated data slicing with index transformation for accurate positioning</li>
53
+ * <li>Nested property resolution with array mapping and filtering</li>
54
+ * <li>Data synchronization using efficient object reference and length comparison</li>
55
+ * <li>Event-driven communication pattern with parent components</li>
56
+ * <li>Lifecycle management with OnInit and OnChanges implementations</li>
57
+ * <li>Conditional action execution based on disabled state flags</li>
58
+ * <li>Automatic pagination state management and validation</li>
59
+ * </ul>
60
+ *
61
+ * <p><strong>Event Handling</strong></p>
62
+ * <ul>
63
+ * <li><b>onModify:</b> Emits when a row modification is requested, includes index, item data, and disabled state</li>
64
+ * <li><b>onNewItemAdded:</b> Emits when a new item is added to the table</li>
65
+ * <li><b>onItemRemoved:</b> Emits when an item is removed, includes index, data size, removed item, and disabled state</li>
66
+ * </ul>
67
+ *
68
+ * <p><strong>Configuration Options</strong></p>
69
+ * <ul>
70
+ * <li>PageSize: Number of rows per page (default: 5)</li>
71
+ * <li>PageSizeOptions: Array of available page size options</li>
72
+ * <li>TableHeaders: Column header definitions with labels and alignment</li>
73
+ * <li>TableColumns: Column data definitions with field names, alignment, and colors</li>
74
+ * <li>EnableActionColumn: Toggle visibility of action column</li>
75
+ * <li>EnableButtonDelete: Control delete button visibility</li>
76
+ * <li>EnableButtonModify: Control modify button visibility</li>
77
+ * <li>DisableRemove: Prevent item removal while maintaining event emission</li>
78
+ * <li>DisableModify: Prevent item modification while maintaining event emission</li>
79
+ * </ul>
80
+ *
81
+ * <p><strong>Authorship</strong></p>
82
+ * <ul>
83
+ * <li><b>Author:</b> Dileesha Ekanayake</li>
84
+ * <li><b>Email:</b> dileesha.r.ekanayake@gmail.com</li>
85
+ * <li><b>Created:</b> 2024</li>
86
+ * <li><b>Version:</b> 1.0.0</li>
87
+ * <li><b>Responsibility:</b> Design, implementation, and documentation of the data table component
88
+ * with Angular Material integration. Provides comprehensive table functionality including
89
+ * pagination, nested property access, dynamic styling, CRUD operations, and seamless form
90
+ * control integration with reactive forms support. Ensures robust data synchronization,
91
+ * event-driven architecture, and flexible configuration options for diverse use cases.</li>
92
+ * </ul>
93
+ */
16
94
  declare class AvDataTable implements OnInit, OnChanges, ControlValueAccessor {
17
95
  PageSize: number;
18
96
  PageSizeOptions: Array<number>;
@@ -43,6 +121,17 @@ declare class AvDataTable implements OnInit, OnChanges, ControlValueAccessor {
43
121
  DisableRemove: boolean;
44
122
  DisableModify: boolean;
45
123
  gridTemplateColumns: string;
124
+ sortField: string;
125
+ sortDirection: 'asc' | 'desc' | '';
126
+ globalSearchTerm: string;
127
+ columnFilters: Map<string, {
128
+ matchMode: 'all' | 'any';
129
+ rules: {
130
+ operator: string;
131
+ value: string;
132
+ }[];
133
+ }>;
134
+ processedData: any[];
46
135
  private onChange;
47
136
  private onTouched;
48
137
  constructor();
@@ -206,8 +295,22 @@ declare class AvDataTable implements OnInit, OnChanges, ControlValueAccessor {
206
295
  * @return {void} Does not return a value.
207
296
  */
208
297
  writeValue(value: any[]): void;
298
+ toggleSort(field: string): void;
299
+ applyAllFilters(): void;
300
+ clearAllFilters(): void;
301
+ updateGlobalSearch(event: any): void;
302
+ setColumnFilter(field: string, filter: any): void;
303
+ getColumnFilter(field: string): {
304
+ matchMode: 'all' | 'any';
305
+ rules: {
306
+ operator: string;
307
+ value: string;
308
+ }[];
309
+ };
310
+ hasActiveFilter(field: string): boolean;
209
311
  static ɵfac: i0.ɵɵFactoryDeclaration<AvDataTable, never>;
210
312
  static ɵcmp: i0.ɵɵComponentDeclaration<AvDataTable, "av-data-table", never, { "PageSize": { "alias": "PageSize"; "required": false; }; "PageSizeOptions": { "alias": "PageSizeOptions"; "required": false; }; "currentPage": { "alias": "currentPage"; "required": false; }; "TableHeaders": { "alias": "TableHeaders"; "required": false; }; "TableColumns": { "alias": "TableColumns"; "required": false; }; "Data": { "alias": "Data"; "required": false; }; "EnableActionColumn": { "alias": "EnableActionColumn"; "required": false; }; "EnableButtonDelete": { "alias": "EnableButtonDelete"; "required": false; }; "EnableButtonModify": { "alias": "EnableButtonModify"; "required": false; }; "DisableRemove": { "alias": "DisableRemove"; "required": false; }; "DisableModify": { "alias": "DisableModify"; "required": false; }; }, { "onModify": "onModify"; "onNewItemAdded": "onNewItemAdded"; "onItemRemoved": "onItemRemoved"; }, never, never, true, never>;
211
313
  }
212
314
 
213
315
  export { AvDataTable };
316
+ export type { Columns, Headers };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avoraui/av-data-table",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "A customizable Angular Data Table component",
5
5
  "keywords": [
6
6
  "angular",
Binary file