@avoraui/av-data-table 0.0.2

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 ADDED
@@ -0,0 +1,312 @@
1
+ # AvDataTable Component (AvoraUI)
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.
4
+
5
+ ## Features
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
16
+
17
+ ## Dependencies
18
+
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
+
27
+ ## Installation
28
+
29
+ 1. Copy the component files to your Angular project
30
+ 2. Import the component in your module or standalone component
31
+ 3. Ensure you have Angular Material (for pagination) installed:
32
+
33
+ ```bash
34
+ ng add @angular/material
35
+ ```
36
+
37
+ ## Basic Usage
38
+
39
+ ### 1. Import and Configure
40
+
41
+ ```typescript
42
+ import { AvDataTable } from './path/to/av-data-table';
43
+
44
+ @Component({
45
+ selector: 'app-example',
46
+ template: `
47
+ <av-data-table
48
+ [TableHeaders]="headers"
49
+ [TableColumns]="columns"
50
+ [Data]="tableData"
51
+ [PageSize]="10"
52
+ [PageSizeOptions]="[5, 10, 25, 50]"
53
+ [EnableActionColumn]="true"
54
+ [EnableButtonDelete]="true"
55
+ [EnableButtonModify]="true"
56
+ (onModify)="handleModify($event)"
57
+ (onItemRemoved)="handleRemove($event)"
58
+ (onNewItemAdded)="handleAdd($event)">
59
+ </av-data-table>
60
+ `
61
+ })
62
+ export class ExampleComponent {
63
+ // Component implementation
64
+ }
65
+ ```
66
+
67
+ ### 2. Define Headers and Columns
68
+
69
+ ```typescript
70
+ export interface Headers {
71
+ label: string;
72
+ align?: 'left' | 'center' | 'right';
73
+ }
74
+
75
+ export interface Columns {
76
+ field: string;
77
+ align?: 'left' | 'center' | 'right';
78
+ color?: string;
79
+ }
80
+ export class ExampleComponent {
81
+ headers: Headers[] = [
82
+ { label: 'Name', align: 'left' },
83
+ { label: 'Email', align: 'left' },
84
+ { label: 'Department', align: 'left' },
85
+ { label: 'Action', align: 'center' }
86
+ ];
87
+
88
+ columns: Columns[] = [
89
+ { field: 'name', align: 'left', color: '#3182ce' },
90
+ { field: 'email', align: 'left' },
91
+ { field: 'department.name', align: 'left' }, // Nested property
92
+ ];
93
+
94
+ tableData = [
95
+ {
96
+ name: 'Kasun Perera',
97
+ email: 'kasun.perera@company.lk',
98
+ department: { name: 'Information Technology', code: 'IT' }
99
+ },
100
+ {
101
+ name: 'Nimali Fernando',
102
+ email: 'nimali.fernando@company.lk',
103
+ department: { name: 'Human Resources', code: 'HR' }
104
+ },
105
+ {
106
+ name: 'Rajitha Silva',
107
+ email: 'rajitha.silva@company.lk',
108
+ department: { name: 'Finance & Accounting', code: 'FIN' }
109
+ },
110
+ {
111
+ name: 'Chaminda Jayawardena',
112
+ email: 'chaminda.j@company.lk',
113
+ department: { name: 'Marketing & Sales', code: 'MKT' }
114
+ }
115
+ ];
116
+ }
117
+ ```
118
+
119
+ ## API Reference
120
+
121
+ ### Input Properties
122
+
123
+ | Property | Type | Default | Description |
124
+ |----------|------|---------|-------------|
125
+ | `TableHeaders` | `Headers[]` | `[]` | Array of header configurations |
126
+ | `TableColumns` | `Columns[]` | `[]` | Array of column configurations |
127
+ | `Data` | `any[]` | `[]` | Data source for the table |
128
+ | `PageSize` | `number` | `5` | Number of items per page |
129
+ | `PageSizeOptions` | `number[]` | `[]` | Available page size options |
130
+ | `currentPage` | `number` | `0` | Current active page index |
131
+ | `EnableActionColumn` | `boolean` | `false` | Show/hide action column |
132
+ | `EnableButtonDelete` | `boolean` | `false` | Enable delete button in actions |
133
+ | `EnableButtonModify` | `boolean` | `false` | Enable modify button in actions |
134
+ | `DisableRemove` | `boolean` | `false` | Disable remove functionality |
135
+ | `DisableModify` | `boolean` | `false` | Disable modify functionality |
136
+
137
+ ### Output Events
138
+
139
+ | Event | Type | Description |
140
+ |-------|------|-------------|
141
+ | `onModify` | `EventEmitter<{index: number, modifiedItem: any, disabled: boolean}>` | Emitted when modify button is clicked - allows custom handling like opening edit forms, viewing details, etc. |
142
+ | `onItemRemoved` | `EventEmitter<{index: number, dataSize: number, removedItem: any, disabled: boolean}>` | Emitted when delete button is clicked - allows custom removal logic, confirmations, API calls, etc. |
143
+ | `onNewItemAdded` | `EventEmitter<{index: number, dataSize: number, removedItem: any}>` | Emitted when new item events occur - allows custom add item logic |
144
+
145
+ ### Interfaces
146
+
147
+ ```typescript
148
+ interface Headers {
149
+ label: string;
150
+ align?: 'left' | 'center' | 'right';
151
+ }
152
+
153
+ interface Columns {
154
+ field: string;
155
+ align?: 'left' | 'center' | 'right';
156
+ color?: string;
157
+ }
158
+ ```
159
+
160
+ ## Advanced Usage
161
+
162
+ ### Nested Object Properties
163
+
164
+ The component supports accessing nested object properties using dot notation:
165
+
166
+ ```typescript
167
+ columns: Columns[] = [
168
+ { field: 'employee.profile.firstName' },
169
+ { field: 'employee.contact.address.city' },
170
+ { field: 'employee.roles.name' }, // Will join array values with commas
171
+ { field: 'department.name' } // Access nested department name
172
+ ];
173
+ ```
174
+
175
+ ### Reactive Forms Integration
176
+
177
+ The component implements `ControlValueAccessor`, making it compatible with Angular Reactive Forms:
178
+
179
+ ```typescript
180
+ import { FormBuilder, FormGroup } from '@angular/forms';
181
+
182
+ export class ExampleComponent {
183
+ form: FormGroup;
184
+
185
+ constructor(private fb: FormBuilder) {
186
+ this.form = this.fb.group({
187
+ tableData: [[]] // Initial empty array
188
+ });
189
+ }
190
+ }
191
+ ```
192
+
193
+ ```html
194
+ <form [formGroup]="form">
195
+ <av-data-table
196
+ formControlName="tableData"
197
+ [TableHeaders]="headers"
198
+ [TableColumns]="columns">
199
+ </av-data-table>
200
+ </form>
201
+ ```
202
+
203
+ ### Event Handling
204
+
205
+ The component provides flexible event handlers that allow you to implement custom business logic:
206
+
207
+ ```typescript
208
+ handleModify(event: {index: number, modifiedItem: any, disabled: boolean}) {
209
+ if (event.disabled) {
210
+ console.log('Modify action is disabled');
211
+ return;
212
+ }
213
+
214
+ // Example: Open edit dialog with the selected row data
215
+ console.log('Modifying employee:', event.modifiedItem.name);
216
+ console.log('Employee data:', event.modifiedItem);
217
+
218
+ // You can:
219
+ // - Open a modal/dialog for editing
220
+ // - Navigate to an edit page
221
+ // - Show detailed view
222
+ // - Perform any custom logic with the row data
223
+ this.openEditDialog(event.modifiedItem, event.index);
224
+ }
225
+
226
+ handleRemove(event: {index: number, dataSize: number, removedItem: any, disabled: boolean}) {
227
+ if (event.disabled) {
228
+ console.log('Remove action is disabled');
229
+ return;
230
+ }
231
+
232
+ // Example: Custom confirmation and API call
233
+ const employee = event.removedItem;
234
+ const confirmed = confirm(`Are you sure you want to remove ${employee.name}?`);
235
+
236
+ if (confirmed) {
237
+ // You can:
238
+ // - Make API calls to delete from server
239
+ // - Update other related data
240
+ // - Show success/error messages
241
+ // - Perform cleanup operations
242
+ console.log(`${employee.name} removed from ${employee.department.name}`);
243
+ console.log('Remaining employees:', event.dataSize);
244
+
245
+ // Example API call
246
+ this.employeeService.deleteEmployee(employee.id).subscribe({
247
+ next: () => console.log('Successfully deleted from server'),
248
+ error: (err) => console.error('Deletion failed:', err)
249
+ });
250
+ }
251
+ }
252
+
253
+ handleAdd(event: {index: number, dataSize: number}) {
254
+ // Example: Trigger add new employee flow
255
+ console.log('Adding new employee...');
256
+
257
+ // You can:
258
+ // - Open add employee dialog
259
+ // - Navigate to create page
260
+ // - Show form modal
261
+ // - Perform any custom add logic
262
+ this.openAddEmployeeDialog();
263
+ }
264
+ ```
265
+
266
+ ### Custom Styling
267
+
268
+ The component uses CSS classes that you can override:
269
+
270
+ ```css
271
+ /* Header alignment */
272
+ .header-text-left { text-align: left; }
273
+ .header-text-center { text-align: center; }
274
+ .header-text-right { text-align: right; }
275
+
276
+ /* Data cell alignment */
277
+ .text-left { text-align: left; }
278
+ .text-center { text-align: center; }
279
+ .text-right { text-align: right; }
280
+
281
+ /* Grid layout customization */
282
+ .table-grid {
283
+ display: grid;
284
+ gap: 1rem;
285
+ /* grid-template-columns is set dynamically */
286
+ }
287
+ ```
288
+
289
+ ## Requirements
290
+
291
+ - Angular 17+
292
+ - Angular Material (for pagination component)
293
+ - Modern browser with CSS Grid support
294
+
295
+ ## Browser Support
296
+
297
+ - Chrome/Edge 57+
298
+ - Firefox 52+
299
+ - Safari 10.1+
300
+
301
+ ## License
302
+
303
+ This project is licensed under the MIT License - see the LICENSE file for details.
304
+
305
+ ## Changelog
306
+
307
+ ### v0.0.2
308
+ - Initial release
309
+ - Basic table functionality with pagination
310
+ - CRUD operations support
311
+ - Reactive forms integration
312
+ - Nested object property access
Binary file
@@ -0,0 +1,464 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, forwardRef, Input, Output, Component } from '@angular/core';
3
+ import { MatCard, MatCardContent } from '@angular/material/card';
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';
8
+ import { MatPaginator } from '@angular/material/paginator';
9
+
10
+ class AvDataTable {
11
+ PageSize = 5;
12
+ PageSizeOptions = [];
13
+ currentPage = 0;
14
+ TableHeaders = [];
15
+ TableColumns = [];
16
+ TableData = [];
17
+ Data = [];
18
+ EnableActionColumn = false;
19
+ EnableButtonDelete = false;
20
+ EnableButtonModify = false;
21
+ onModify = new EventEmitter();
22
+ onNewItemAdded = new EventEmitter();
23
+ onItemRemoved = new EventEmitter();
24
+ DisableRemove = false;
25
+ DisableModify = false;
26
+ gridTemplateColumns = '';
27
+ onChange = () => { };
28
+ onTouched = () => { };
29
+ constructor() {
30
+ }
31
+ /**
32
+ * Lifecycle hook that is called after Angular has initialized all data-bound properties of a directive.
33
+ * This method is used to perform initialization logic for the component.
34
+ *
35
+ * It executes the following:
36
+ * - Initializes the paginator for managing paginated data display.
37
+ * - Calculates the grid template for layout adjustments.
38
+ * - Synchronizes data sources to ensure the component has up-to-date data.
39
+ *
40
+ * @return {void} This method does not return any value.
41
+ */
42
+ ngOnInit() {
43
+ this.initializePaginator();
44
+ this.calculateGridTemplate();
45
+ this.syncDataSources();
46
+ }
47
+ /**
48
+ * Initializes the paginator configuration. Sets default values for PageSize, PageSizeOptions,
49
+ * and currentPage if they are undefined, null, or invalid. Ensures the PageSize is included
50
+ * in the PageSizeOptions array and sorts the options in ascending order.
51
+ *
52
+ * @return {void} Does not return a value.
53
+ */
54
+ initializePaginator() {
55
+ if (this.PageSize === undefined || this.PageSize === null || this.PageSize <= 0) {
56
+ this.PageSize = 5;
57
+ }
58
+ if (this.PageSizeOptions === undefined || this.PageSizeOptions === null || this.PageSizeOptions.length === 0) {
59
+ this.PageSizeOptions = [5, 10, 20, 50];
60
+ }
61
+ if (this.currentPage === undefined || this.currentPage === null || this.currentPage < 0) {
62
+ this.currentPage = 0;
63
+ }
64
+ // Ensure PageSize is included in PageSizeOptions if not already present
65
+ if (!this.PageSizeOptions.includes(this.PageSize)) {
66
+ this.PageSizeOptions = [...this.PageSizeOptions, this.PageSize].sort((a, b) => a - b);
67
+ }
68
+ }
69
+ /**
70
+ * Handles changes to the component's input properties during the lifecycle of the component.
71
+ *
72
+ * This method is triggered whenever an input property bound to the component changes. It processes
73
+ * updates to properties such as `Data`, `PageSize`, `PageSizeOptions`, and `currentPage`, and
74
+ * ensures the internal state is kept in sync with the new inputs. Additionally, it updates the paginator
75
+ * and adjusts the current page as necessary.
76
+ *
77
+ * @param {SimpleChanges} changes - A collection of SimpleChange objects representing the changed properties.
78
+ * Each `SimpleChange` object provides information like the current and previous values as well as a flag
79
+ * indicating if it is the first change to this input.
80
+ * @return {void} - This method does not return any value.
81
+ */
82
+ ngOnChanges(changes) {
83
+ // Listen for changes in the external Data input
84
+ if (changes['Data'] && !changes['Data'].firstChange) {
85
+ this.syncDataSources();
86
+ // Reset to first page when data changes
87
+ this.currentPage = 0;
88
+ }
89
+ // Handle changes in pagination settings
90
+ if (changes['PageSize'] || changes['PageSizeOptions'] || changes['currentPage']) {
91
+ this.initializePaginator();
92
+ // Validate current page doesn't exceed available pages
93
+ const totalPages = Math.ceil(this.TableData.length / this.PageSize);
94
+ if (this.currentPage >= totalPages && totalPages > 0) {
95
+ this.currentPage = totalPages - 1;
96
+ }
97
+ }
98
+ }
99
+ /**
100
+ * Retrieves a subset of data from the full dataset based on the current page and page size.
101
+ *
102
+ * @return {Array} A portion of the TableData array corresponding to the current page.
103
+ */
104
+ getPaginatedData() {
105
+ const startIndex = this.currentPage * this.PageSize;
106
+ const endIndex = startIndex + this.PageSize;
107
+ return this.TableData.slice(startIndex, endIndex);
108
+ }
109
+ /**
110
+ * Handles changes in the pagination state, such as changing the current page or the page size.
111
+ *
112
+ * @param {PageEvent} event - The event triggered by a pagination action that contains the updated page index and page size.
113
+ * @return {void} This method does not return anything.
114
+ */
115
+ pageChange(event) {
116
+ this.currentPage = event.pageIndex;
117
+ this.PageSize = event.pageSize;
118
+ }
119
+ /**
120
+ * Calculates the actual index in the dataset based on the paginated index.
121
+ *
122
+ * @param {number} paginatedIndex - The index within the current page of paginated data.
123
+ * @return {number} The actual index in the entire dataset.
124
+ */
125
+ getActualIndex(paginatedIndex) {
126
+ return (this.currentPage * this.PageSize) + paginatedIndex;
127
+ }
128
+ /**
129
+ * Synchronizes external data source with the internal TableData.
130
+ * If the external Data differs from the current TableData, the TableData
131
+ * is updated with the values from Data and a change notification is triggered.
132
+ *
133
+ * @return {void} This method does not return a value.
134
+ */
135
+ 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
+ }
147
+ }
148
+ }
149
+ /**
150
+ * Updates the form value by notifying changes and updating external data if available.
151
+ * This method triggers the onChange callback with the current table data
152
+ * and notifies that the form field has been touched. Additionally, it updates
153
+ * the external data reference to ensure proper change detection in the parent components.
154
+ *
155
+ * @return {void} No return value.
156
+ */
157
+ updateFormValue() {
158
+ // Notify the form about the change
159
+ if (this.onChange) {
160
+ this.onChange(this.TableData);
161
+ this.onTouched();
162
+ }
163
+ // Also update the external Data if it exists
164
+ if (this.Data) {
165
+ // Create a new reference to trigger OnChanges in parent components
166
+ this.Data = [...this.TableData];
167
+ }
168
+ }
169
+ /**
170
+ * Calculates and sets the grid template for a layout based on the number of displayed headers
171
+ * and the presence of an action column. The grid template determines the column structure with
172
+ * equal fractions for data columns and a fixed width for the action column if enabled.
173
+ *
174
+ * @return {void} Does not return a value. Sets the `gridTemplateColumns` property to the calculated template.
175
+ */
176
+ calculateGridTemplate() {
177
+ // Calculate grid template based on columns count
178
+ const displayedHeaders = this.getDisplayedHeaders();
179
+ const columnsCount = displayedHeaders.length;
180
+ if (columnsCount === 0)
181
+ return;
182
+ // Create grid template with equal fractions for data columns
183
+ // and fixed width for action column if enabled
184
+ if (this.EnableActionColumn) {
185
+ const dataColumns = columnsCount - 1; // Subtract action column
186
+ this.gridTemplateColumns = dataColumns > 0
187
+ ? `repeat(${dataColumns}, 1fr) 100px`
188
+ : '100px';
189
+ }
190
+ else {
191
+ this.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;
192
+ }
193
+ }
194
+ /**
195
+ * Retrieves the headers that should be displayed in the table.
196
+ * The method includes or excludes the "action" column based on the value of `EnableActionColumn`.
197
+ *
198
+ * @return {TableProp[]} An array of headers to be displayed in the table. If `EnableActionColumn` is false, the "action" column is excluded.
199
+ */
200
+ getDisplayedHeaders() {
201
+ return this.EnableActionColumn
202
+ ? this.TableHeaders
203
+ : this.TableHeaders.filter(h => h.label.toLowerCase() !== 'action');
204
+ }
205
+ /**
206
+ * Generates and returns an object representing CSS classes for a header based on its alignment property.
207
+ *
208
+ * @param {TableProp} header - An object containing header properties, including an alignment property ('center', 'right', or 'left').
209
+ * @return {Object} An object mapping class names to a boolean indicating their applicability based on the alignment of the header.
210
+ */
211
+ getHeaderFieldClasses(header) {
212
+ return {
213
+ 'header-text-center': header.align === 'center',
214
+ 'header-text-right': header.align === 'right',
215
+ 'header-text-left': header.align === 'left',
216
+ };
217
+ }
218
+ /**
219
+ * Generates a mapping of CSS classes for a data field based on the alignment property of the given column.
220
+ *
221
+ * @param {Columns} column - The column object containing alignment information.
222
+ * @return {Object} An object where the keys are class names, and the values are booleans indicating whether each class applies.
223
+ */
224
+ getDataFieldClasses(column) {
225
+ return {
226
+ 'text-center': column.align === 'center',
227
+ 'text-right': column.align === 'right',
228
+ 'text-left': column.align === 'left',
229
+ };
230
+ }
231
+ /**
232
+ * Retrieves the color property from the given column object.
233
+ *
234
+ * @param {Columns} column - The column object containing the color property.
235
+ * @return {string} The color associated with the column.
236
+ */
237
+ getDataFiledColor(column) {
238
+ return column.color;
239
+ }
240
+ /**
241
+ * Retrieves the value of a specified column from the given item.
242
+ *
243
+ * @param {any} item - The object containing the data to extract the value from.
244
+ * @param {Columns} column - The column definition, including the field name or path.
245
+ * @return {string} The value of the specified column for the given item. If the field is not found, an empty string is returned.
246
+ */
247
+ getValueForColumn(item, column) {
248
+ if (!item || !column || !column.field)
249
+ return '';
250
+ // Check if the field has dot notation (e.g., 'gender.name')
251
+ if (column.field.includes('.')) {
252
+ return this.getNestedValue(item, column.field);
253
+ }
254
+ else {
255
+ // Handle standard fields
256
+ return column.field in item ? item[column.field] : '';
257
+ }
258
+ }
259
+ /**
260
+ * Retrieves a nested value from a given object based on a dot-separated path string.
261
+ * If the path does not exist or the value is undefined/null, it returns a placeholder '-'.
262
+ * If accessing an array, it maps the values at the given key and processes them.
263
+ *
264
+ * @param {any} object - The object from which the nested value is extracted.
265
+ * @param {string} path - The dot-separated string representing the path to the desired value.
266
+ * @return {any} The value found at the given path, an array joined into a string, or '-' if not found.
267
+ */
268
+ getNestedValue(object, path) {
269
+ // Return early if object is null or undefined
270
+ if (!object)
271
+ return '-';
272
+ // Split the path into individual keys (e.g., "gender.name" → ["gender", "name"])
273
+ const keys = path.split('.');
274
+ let value = object;
275
+ // Navigate through the object hierarchy
276
+ for (const key of keys) {
277
+ if (value === null || value === undefined) {
278
+ return '-';
279
+ }
280
+ if (Array.isArray(value)) {
281
+ // If we encounter an array, map over its items to extract the property
282
+ value = value.map(item => item[key]).filter(Boolean);
283
+ }
284
+ else {
285
+ // Access the next property level
286
+ value = value[key];
287
+ }
288
+ }
289
+ // Format the final result
290
+ if (Array.isArray(value)) {
291
+ return value.length > 0 ? value.join(', ') : '-';
292
+ }
293
+ return value !== null && value !== undefined ? value : '-';
294
+ }
295
+ /**
296
+ * Removes an item from the data table at the specified index.
297
+ *
298
+ * @param {number} actualIndex - The index of the item to remove from the data table.
299
+ * @return {void} This method does not return a value.
300
+ */
301
+ removeItem(actualIndex) {
302
+ // Check disable condition FIRST - before touching any data
303
+ if (this.DisableRemove) {
304
+ // Don't remove from TableData, just emit event for parent to handle
305
+ this.onItemRemoved.emit({
306
+ index: actualIndex,
307
+ dataSize: this.TableData.length,
308
+ removedItem: this.TableData[actualIndex], // Get the item that would be removed
309
+ disabled: true
310
+ });
311
+ return; // Exit early - don't modify TableData
312
+ }
313
+ else {
314
+ // Only proceed with actual removal if not disabled
315
+ const newData = [...this.TableData];
316
+ const removedData = newData[actualIndex]; // Get the item being removed
317
+ newData.splice(actualIndex, 1);
318
+ this.TableData = newData;
319
+ // Rest of your existing logic...
320
+ const totalPages = Math.ceil(this.TableData.length / this.PageSize);
321
+ if (this.currentPage >= totalPages && this.currentPage > 0) {
322
+ this.currentPage = totalPages - 1;
323
+ }
324
+ if (this.TableData.length === 0) {
325
+ this.onChange([removedData]);
326
+ }
327
+ else {
328
+ this.updateFormValue();
329
+ }
330
+ this.onItemRemoved.emit({
331
+ index: actualIndex,
332
+ dataSize: this.TableData.length,
333
+ removedItem: removedData,
334
+ disabled: false
335
+ });
336
+ }
337
+ }
338
+ /**
339
+ * Modifies an item at the specified index in the dataset. If the modification is disabled, emits an event without making any changes.
340
+ *
341
+ * @param {number} actualIndex - The index of the item to be modified in the dataset.
342
+ * @return {void} This method does not return a value.
343
+ */
344
+ modifyItem(actualIndex) {
345
+ // Check disable condition FIRST - before any modification logic
346
+ if (this.DisableModify) {
347
+ // Emit event for parent to handle the disabled state
348
+ this.onModify.emit({
349
+ index: actualIndex,
350
+ modifiedItem: this.TableData[actualIndex],
351
+ disabled: true
352
+ });
353
+ return; // Exit early - don't proceed with modification
354
+ }
355
+ // Only proceed with modification if not disabled
356
+ const modifiedItem = this.TableData[actualIndex];
357
+ this.onModify.emit({
358
+ index: actualIndex,
359
+ modifiedItem,
360
+ disabled: false
361
+ });
362
+ }
363
+ // ControlValueAccessor interface implementation
364
+ /**
365
+ * Registers a callback function to be called whenever the value changes.
366
+ *
367
+ * @param {any} fn - The function to be executed on a value change.
368
+ * @return {void} This method does not return a value.
369
+ */
370
+ registerOnChange(fn) {
371
+ this.onChange = fn;
372
+ }
373
+ /**
374
+ * Registers a callback function that should be called when the control is touched.
375
+ *
376
+ * @param {any} fn - The callback function to execute when the control is touched.
377
+ * @return {void} This method does not return a value.
378
+ */
379
+ registerOnTouched(fn) {
380
+ this.onTouched = fn;
381
+ }
382
+ /**
383
+ * Writes a new value to the table data and resets to the first page.
384
+ *
385
+ * @param {any[]} value - The array of data to be written to the table.
386
+ * @return {void} Does not return a value.
387
+ */
388
+ writeValue(value) {
389
+ if (value) {
390
+ this.TableData = [...value];
391
+ // Reset to first page when new data is written
392
+ this.currentPage = 0;
393
+ }
394
+ else {
395
+ this.TableData = [];
396
+ this.currentPage = 0;
397
+ }
398
+ }
399
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AvDataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
400
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.2", type: AvDataTable, isStandalone: true, selector: "aur-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
+ {
402
+ provide: NG_VALUE_ACCESSOR,
403
+ useExisting: forwardRef(() => AvDataTable),
404
+ multi: true
405
+ }
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"] }] });
407
+ }
408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: AvDataTable, decorators: [{
409
+ type: Component,
410
+ args: [{ selector: 'aur-data-table', providers: [
411
+ {
412
+ provide: NG_VALUE_ACCESSOR,
413
+ useExisting: forwardRef(() => AvDataTable),
414
+ multi: true
415
+ }
416
+ ], imports: [
417
+ MatCard,
418
+ MatCardContent,
419
+ MatIcon,
420
+ MatIconButton,
421
+ NgClass,
422
+ NgStyle,
423
+ 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"] }]
425
+ }], ctorParameters: () => [], propDecorators: { PageSize: [{
426
+ type: Input
427
+ }], PageSizeOptions: [{
428
+ type: Input
429
+ }], currentPage: [{
430
+ type: Input
431
+ }], TableHeaders: [{
432
+ type: Input
433
+ }], TableColumns: [{
434
+ type: Input
435
+ }], Data: [{
436
+ type: Input
437
+ }], EnableActionColumn: [{
438
+ type: Input
439
+ }], EnableButtonDelete: [{
440
+ type: Input
441
+ }], EnableButtonModify: [{
442
+ type: Input
443
+ }], onModify: [{
444
+ type: Output
445
+ }], onNewItemAdded: [{
446
+ type: Output
447
+ }], onItemRemoved: [{
448
+ type: Output
449
+ }], DisableRemove: [{
450
+ type: Input
451
+ }], DisableModify: [{
452
+ type: Input
453
+ }] } });
454
+
455
+ /*
456
+ * Public API Surface of data-table
457
+ */
458
+
459
+ /**
460
+ * Generated bundle index. Do not edit.
461
+ */
462
+
463
+ export { AvDataTable };
464
+ //# sourceMappingURL=avoraui-av-data-table.mjs.map
@@ -0,0 +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: 'aur-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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"}
package/index.d.ts ADDED
@@ -0,0 +1,213 @@
1
+ import * as i0 from '@angular/core';
2
+ import { OnInit, OnChanges, EventEmitter, SimpleChanges } from '@angular/core';
3
+ import { ControlValueAccessor } from '@angular/forms';
4
+ import { PageEvent } from '@angular/material/paginator';
5
+
6
+ interface Headers {
7
+ label: string;
8
+ align: 'left' | 'center' | 'right';
9
+ }
10
+ interface Columns {
11
+ field: string;
12
+ align: 'left' | 'center' | 'right';
13
+ color?: string;
14
+ }
15
+
16
+ declare class AvDataTable implements OnInit, OnChanges, ControlValueAccessor {
17
+ PageSize: number;
18
+ PageSizeOptions: Array<number>;
19
+ currentPage: number;
20
+ TableHeaders: Headers[];
21
+ TableColumns: Columns[];
22
+ protected TableData: any[];
23
+ Data: any[];
24
+ EnableActionColumn: boolean;
25
+ EnableButtonDelete: boolean;
26
+ EnableButtonModify: boolean;
27
+ onModify: EventEmitter<{
28
+ index: number;
29
+ modifiedItem: any;
30
+ disabled: boolean;
31
+ }>;
32
+ onNewItemAdded: EventEmitter<{
33
+ index: number;
34
+ dataSize: number;
35
+ removedItem: any;
36
+ }>;
37
+ onItemRemoved: EventEmitter<{
38
+ index: number;
39
+ dataSize: number;
40
+ removedItem: any;
41
+ disabled: boolean;
42
+ }>;
43
+ DisableRemove: boolean;
44
+ DisableModify: boolean;
45
+ gridTemplateColumns: string;
46
+ private onChange;
47
+ private onTouched;
48
+ constructor();
49
+ /**
50
+ * Lifecycle hook that is called after Angular has initialized all data-bound properties of a directive.
51
+ * This method is used to perform initialization logic for the component.
52
+ *
53
+ * It executes the following:
54
+ * - Initializes the paginator for managing paginated data display.
55
+ * - Calculates the grid template for layout adjustments.
56
+ * - Synchronizes data sources to ensure the component has up-to-date data.
57
+ *
58
+ * @return {void} This method does not return any value.
59
+ */
60
+ ngOnInit(): void;
61
+ /**
62
+ * Initializes the paginator configuration. Sets default values for PageSize, PageSizeOptions,
63
+ * and currentPage if they are undefined, null, or invalid. Ensures the PageSize is included
64
+ * in the PageSizeOptions array and sorts the options in ascending order.
65
+ *
66
+ * @return {void} Does not return a value.
67
+ */
68
+ initializePaginator(): void;
69
+ /**
70
+ * Handles changes to the component's input properties during the lifecycle of the component.
71
+ *
72
+ * This method is triggered whenever an input property bound to the component changes. It processes
73
+ * updates to properties such as `Data`, `PageSize`, `PageSizeOptions`, and `currentPage`, and
74
+ * ensures the internal state is kept in sync with the new inputs. Additionally, it updates the paginator
75
+ * and adjusts the current page as necessary.
76
+ *
77
+ * @param {SimpleChanges} changes - A collection of SimpleChange objects representing the changed properties.
78
+ * Each `SimpleChange` object provides information like the current and previous values as well as a flag
79
+ * indicating if it is the first change to this input.
80
+ * @return {void} - This method does not return any value.
81
+ */
82
+ ngOnChanges(changes: SimpleChanges): void;
83
+ /**
84
+ * Retrieves a subset of data from the full dataset based on the current page and page size.
85
+ *
86
+ * @return {Array} A portion of the TableData array corresponding to the current page.
87
+ */
88
+ getPaginatedData(): Array<any>;
89
+ /**
90
+ * Handles changes in the pagination state, such as changing the current page or the page size.
91
+ *
92
+ * @param {PageEvent} event - The event triggered by a pagination action that contains the updated page index and page size.
93
+ * @return {void} This method does not return anything.
94
+ */
95
+ pageChange(event: PageEvent): void;
96
+ /**
97
+ * Calculates the actual index in the dataset based on the paginated index.
98
+ *
99
+ * @param {number} paginatedIndex - The index within the current page of paginated data.
100
+ * @return {number} The actual index in the entire dataset.
101
+ */
102
+ getActualIndex(paginatedIndex: number): number;
103
+ /**
104
+ * Synchronizes external data source with the internal TableData.
105
+ * If the external Data differs from the current TableData, the TableData
106
+ * is updated with the values from Data and a change notification is triggered.
107
+ *
108
+ * @return {void} This method does not return a value.
109
+ */
110
+ syncDataSources(): void;
111
+ /**
112
+ * Updates the form value by notifying changes and updating external data if available.
113
+ * This method triggers the onChange callback with the current table data
114
+ * and notifies that the form field has been touched. Additionally, it updates
115
+ * the external data reference to ensure proper change detection in the parent components.
116
+ *
117
+ * @return {void} No return value.
118
+ */
119
+ updateFormValue(): void;
120
+ /**
121
+ * Calculates and sets the grid template for a layout based on the number of displayed headers
122
+ * and the presence of an action column. The grid template determines the column structure with
123
+ * equal fractions for data columns and a fixed width for the action column if enabled.
124
+ *
125
+ * @return {void} Does not return a value. Sets the `gridTemplateColumns` property to the calculated template.
126
+ */
127
+ calculateGridTemplate(): void;
128
+ /**
129
+ * Retrieves the headers that should be displayed in the table.
130
+ * The method includes or excludes the "action" column based on the value of `EnableActionColumn`.
131
+ *
132
+ * @return {TableProp[]} An array of headers to be displayed in the table. If `EnableActionColumn` is false, the "action" column is excluded.
133
+ */
134
+ getDisplayedHeaders(): Headers[];
135
+ /**
136
+ * Generates and returns an object representing CSS classes for a header based on its alignment property.
137
+ *
138
+ * @param {TableProp} header - An object containing header properties, including an alignment property ('center', 'right', or 'left').
139
+ * @return {Object} An object mapping class names to a boolean indicating their applicability based on the alignment of the header.
140
+ */
141
+ getHeaderFieldClasses(header: Headers): object;
142
+ /**
143
+ * Generates a mapping of CSS classes for a data field based on the alignment property of the given column.
144
+ *
145
+ * @param {Columns} column - The column object containing alignment information.
146
+ * @return {Object} An object where the keys are class names, and the values are booleans indicating whether each class applies.
147
+ */
148
+ getDataFieldClasses(column: Columns): object;
149
+ /**
150
+ * Retrieves the color property from the given column object.
151
+ *
152
+ * @param {Columns} column - The column object containing the color property.
153
+ * @return {string} The color associated with the column.
154
+ */
155
+ getDataFiledColor(column: Columns): any;
156
+ /**
157
+ * Retrieves the value of a specified column from the given item.
158
+ *
159
+ * @param {any} item - The object containing the data to extract the value from.
160
+ * @param {Columns} column - The column definition, including the field name or path.
161
+ * @return {string} The value of the specified column for the given item. If the field is not found, an empty string is returned.
162
+ */
163
+ getValueForColumn(item: any, column: Columns): string;
164
+ /**
165
+ * Retrieves a nested value from a given object based on a dot-separated path string.
166
+ * If the path does not exist or the value is undefined/null, it returns a placeholder '-'.
167
+ * If accessing an array, it maps the values at the given key and processes them.
168
+ *
169
+ * @param {any} object - The object from which the nested value is extracted.
170
+ * @param {string} path - The dot-separated string representing the path to the desired value.
171
+ * @return {any} The value found at the given path, an array joined into a string, or '-' if not found.
172
+ */
173
+ getNestedValue(object: any, path: string): any;
174
+ /**
175
+ * Removes an item from the data table at the specified index.
176
+ *
177
+ * @param {number} actualIndex - The index of the item to remove from the data table.
178
+ * @return {void} This method does not return a value.
179
+ */
180
+ removeItem(actualIndex: number): void;
181
+ /**
182
+ * Modifies an item at the specified index in the dataset. If the modification is disabled, emits an event without making any changes.
183
+ *
184
+ * @param {number} actualIndex - The index of the item to be modified in the dataset.
185
+ * @return {void} This method does not return a value.
186
+ */
187
+ modifyItem(actualIndex: number): void;
188
+ /**
189
+ * Registers a callback function to be called whenever the value changes.
190
+ *
191
+ * @param {any} fn - The function to be executed on a value change.
192
+ * @return {void} This method does not return a value.
193
+ */
194
+ registerOnChange(fn: any): void;
195
+ /**
196
+ * Registers a callback function that should be called when the control is touched.
197
+ *
198
+ * @param {any} fn - The callback function to execute when the control is touched.
199
+ * @return {void} This method does not return a value.
200
+ */
201
+ registerOnTouched(fn: any): void;
202
+ /**
203
+ * Writes a new value to the table data and resets to the first page.
204
+ *
205
+ * @param {any[]} value - The array of data to be written to the table.
206
+ * @return {void} Does not return a value.
207
+ */
208
+ writeValue(value: any[]): void;
209
+ static ɵfac: i0.ɵɵFactoryDeclaration<AvDataTable, never>;
210
+ static ɵcmp: i0.ɵɵComponentDeclaration<AvDataTable, "aur-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
+ }
212
+
213
+ export { AvDataTable };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@avoraui/av-data-table",
3
+ "version": "0.0.2",
4
+ "description": "A customizable Angular Data Table component",
5
+ "keywords": [
6
+ "angular",
7
+ "table",
8
+ "data-table"
9
+ ],
10
+ "author": "Dileesha Ekanayake",
11
+ "license": "MIT",
12
+ "peerDependencies": {
13
+ "@angular/common": "^20.1.0",
14
+ "@angular/core": "^20.1.0"
15
+ },
16
+ "dependencies": {
17
+ "@angular/material": "^20.1.2",
18
+ "tslib": "^2.3.0"
19
+ },
20
+ "sideEffects": false,
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "module": "fesm2022/avoraui-av-data-table.mjs",
25
+ "typings": "index.d.ts",
26
+ "exports": {
27
+ "./package.json": {
28
+ "default": "./package.json"
29
+ },
30
+ ".": {
31
+ "types": "./index.d.ts",
32
+ "default": "./fesm2022/avoraui-av-data-table.mjs"
33
+ }
34
+ }
35
+ }