@nlabtech/nlabs-grid 1.1.0

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,638 @@
1
+ # nlabs-grid
2
+
3
+ A modern, feature-rich, and highly customizable Angular data grid component built for Angular 21+ with full theme support and enterprise-grade functionality.
4
+
5
+ ## Features
6
+
7
+ ### Core Features
8
+ - **Modern Angular**: Built with Angular 21+ using standalone components
9
+ - **TypeScript**: Full TypeScript support with type safety
10
+ - **Reactive Design**: Built with signals and reactive patterns
11
+ - **Theme Support**: Built-in light/dark theme with customizable CSS variables
12
+ - **Responsive**: Mobile-friendly and responsive design
13
+
14
+ ### Data Management
15
+ - **Lazy Loading**: Server-side pagination with OData adapter
16
+ - **Local Data**: Client-side data handling
17
+ - **Sorting**: Multi-column sorting support
18
+ - **Filtering**: Per-column filtering with various filter types
19
+ - **Pagination**: Configurable page sizes and navigation
20
+
21
+ ### UI Features
22
+ - **Row Selection**: Single and multi-row selection with custom checkboxes
23
+ - **Column Reordering**: Drag-and-drop column reordering
24
+ - **Column Resizing**: Interactive column width adjustment
25
+ - **Column Chooser**: Show/hide columns dynamically
26
+ - **Custom Templates**: Support for custom cell, header, footer, and action templates
27
+ - **Actions Column**: Customizable action buttons (edit, delete, view, etc.)
28
+ - **Empty State**: Customizable empty data message
29
+
30
+ ### Advanced Features
31
+ - **Custom Checkbox Design**: Modern, corporate-style checkboxes with smooth animations
32
+ - **Actions Template**: Fully customizable action buttons via ng-template
33
+ - **Global Search**: Search across all columns
34
+ - **Export Options**: Excel and PDF export capabilities
35
+ - **State Management**: Preserve grid state (sorting, filtering, pagination)
36
+
37
+ ## Links
38
+
39
+ - **GitHub Repository**: [https://github.com/NlabsNpmPackages/nlabs-grid](https://github.com/NlabsNpmPackages/nlabs-grid)
40
+ - **Example Usage**: [https://github.com/NlabsGlobalFullStack/nlabs-data-grid-example](https://github.com/NlabsGlobalFullStack/nlabs-data-grid-example)
41
+ - **npm Package**: [https://www.npmjs.com/package/nlabs-grid](https://www.npmjs.com/package/nlabs-grid)
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ npm install nlabs-grid
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ### 1. Import the Component
52
+
53
+ ```typescript
54
+ import { Component } from '@angular/core';
55
+ import { DataGridComponent, GridConfig, ODataAdapter } from 'nlabs-grid';
56
+ import { HttpClient } from '@angular/common/http';
57
+
58
+ @Component({
59
+ selector: 'app-root',
60
+ standalone: true,
61
+ imports: [DataGridComponent],
62
+ template: `
63
+ <nlabs-data-grid
64
+ [config]="gridConfig"
65
+ [adapter]="dataAdapter"
66
+ [autoLoad]="true"
67
+ [lazy]="true"
68
+ [theme]="'dark'"
69
+ />
70
+ `
71
+ })
72
+ export class AppComponent {
73
+ gridConfig: GridConfig = {
74
+ columns: [
75
+ { field: 'id', header: 'ID', sortable: true, width: '80px' },
76
+ { field: 'name', header: 'Name', sortable: true, filterable: true },
77
+ { field: 'email', header: 'Email', sortable: true, filterable: true }
78
+ ],
79
+ pageSize: 10,
80
+ sortable: true,
81
+ filterable: true
82
+ };
83
+
84
+ dataAdapter: ODataAdapter;
85
+
86
+ constructor(private http: HttpClient) {
87
+ this.dataAdapter = new ODataAdapter(http, 'https://api.example.com/odata/Users');
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### 2. Basic Configuration
93
+
94
+ ```typescript
95
+ gridConfig: GridConfig = {
96
+ columns: [
97
+ {
98
+ field: 'id',
99
+ header: 'ID',
100
+ sortable: true,
101
+ filterable: false,
102
+ width: '80px',
103
+ type: 'number'
104
+ },
105
+ {
106
+ field: 'name',
107
+ header: 'Name',
108
+ sortable: true,
109
+ filterable: true,
110
+ width: '200px'
111
+ },
112
+ {
113
+ field: 'active',
114
+ header: 'Active',
115
+ type: 'boolean',
116
+ format: (value: boolean) => value ? '✓ Active' : '✗ Inactive'
117
+ }
118
+ ],
119
+ pageSize: 10,
120
+ pageSizeOptions: [5, 10, 25, 50, 100],
121
+ sortable: true,
122
+ filterable: true,
123
+ selectable: true,
124
+ multiSelect: true,
125
+ showCheckboxColumn: true,
126
+ checkboxColumnWidth: '60px',
127
+ showActions: true,
128
+ actionsHeader: 'Actions',
129
+ actionsWidth: '180px',
130
+ reorderable: true,
131
+ resizable: true,
132
+ showHeader: true,
133
+ showFooter: true,
134
+ emptyMessage: 'No records found'
135
+ };
136
+ ```
137
+
138
+ ## Advanced Usage
139
+
140
+ ### Selection with Custom Checkboxes
141
+
142
+ ```typescript
143
+ // Enable selection
144
+ gridConfig: GridConfig = {
145
+ selectable: true,
146
+ multiSelect: true,
147
+ showCheckboxColumn: true,
148
+ checkboxColumnWidth: '60px',
149
+ // ... other config
150
+ };
151
+
152
+ // Handle selection events
153
+ onRowSelect(row: any): void {
154
+ console.log('Row selected:', row);
155
+ }
156
+
157
+ onRowUnselect(row: any): void {
158
+ console.log('Row unselected:', row);
159
+ }
160
+ ```
161
+
162
+ ```html
163
+ <nlabs-data-grid
164
+ [config]="gridConfig"
165
+ [adapter]="dataAdapter"
166
+ (rowSelect)="onRowSelect($event)"
167
+ (rowUnselect)="onRowUnselect($event)"
168
+ />
169
+ ```
170
+
171
+ ### Custom Actions Column
172
+
173
+ ```typescript
174
+ import { GridColumnCommandTemplateDirective } from 'nlabs-grid';
175
+
176
+ @Component({
177
+ imports: [DataGridComponent, GridColumnCommandTemplateDirective],
178
+ // ...
179
+ })
180
+ ```
181
+
182
+ ```html
183
+ <nlabs-data-grid
184
+ [config]="gridConfig"
185
+ [adapter]="dataAdapter">
186
+
187
+ <!-- Custom Actions Template -->
188
+ <ng-template nlabsGridColumnCommandTemplate="actions" let-row>
189
+ <button class="btn-edit" (click)="onEdit(row)">
190
+ ✏️ Edit
191
+ </button>
192
+ <button class="btn-delete" (click)="onDelete(row)">
193
+ 🗑️ Delete
194
+ </button>
195
+ <button class="btn-view" (click)="onView(row)">
196
+ 👁️ View
197
+ </button>
198
+ </ng-template>
199
+ </nlabs-data-grid>
200
+ ```
201
+
202
+ ### Custom Footer Template
203
+
204
+ ```typescript
205
+ import { GridFooterTemplateDirective } from 'nlabs-grid';
206
+
207
+ @Component({
208
+ imports: [DataGridComponent, GridFooterTemplateDirective],
209
+ // ...
210
+ })
211
+ ```
212
+
213
+ ```html
214
+ <nlabs-data-grid [config]="gridConfig" [adapter]="dataAdapter">
215
+ <ng-template nlabsGridFooterTemplate let-data let-total="total">
216
+ <div class="custom-footer">
217
+ <span>Total Records: {{ total }}</span>
218
+ <span>Showing {{ data.length }} items</span>
219
+ </div>
220
+ </ng-template>
221
+ </nlabs-data-grid>
222
+ ```
223
+
224
+ ### OData Integration
225
+
226
+ ```typescript
227
+ import { ODataAdapter } from 'nlabs-grid';
228
+
229
+ // Create adapter
230
+ this.odataAdapter = new ODataAdapter<User>(
231
+ this.http,
232
+ 'http://localhost:5210/odata/Users'
233
+ );
234
+
235
+ // Use with grid
236
+ <nlabs-data-grid
237
+ [adapter]="odataAdapter"
238
+ [lazy]="true"
239
+ [autoLoad]="true"
240
+ />
241
+ ```
242
+
243
+ ### REST API Integration
244
+
245
+ For standard REST endpoints (non-OData), use the `RestAdapter`:
246
+
247
+ ```typescript
248
+ import { RestAdapter, RestAdapterConfig } from 'nlabs-grid';
249
+
250
+ // Simple usage - API returns { data: [], total: number }
251
+ this.restAdapter = new RestAdapter<User>(
252
+ this.http,
253
+ 'http://localhost:5000/api/users'
254
+ );
255
+
256
+ // Custom configuration
257
+ this.restAdapter = new RestAdapter<User>(
258
+ this.http,
259
+ 'http://localhost:5000/api/users',
260
+ {
261
+ usePagination: 'page', // Use page/pageSize instead of skip/take
262
+ pageParam: 'page', // Query param name for page
263
+ pageSizeParam: 'limit', // Query param name for page size
264
+ sortParam: 'orderBy', // Query param name for sorting
265
+ dataKey: 'result.items', // Nested path to data array
266
+ totalKey: 'result.totalCount' // Nested path to total count
267
+ }
268
+ );
269
+
270
+ // With custom response mapper for complex APIs
271
+ this.restAdapter = new RestAdapter<User>(
272
+ this.http,
273
+ 'http://localhost:5000/api/users',
274
+ {
275
+ responseMapper: (response) => ({
276
+ data: response.payload.users,
277
+ total: response.meta.pagination.total
278
+ })
279
+ }
280
+ );
281
+ ```
282
+
283
+ #### REST Backend Examples
284
+
285
+ **ASP.NET Core Minimal API:**
286
+
287
+ ```csharp
288
+ // Program.cs
289
+ var builder = WebApplication.CreateBuilder(args);
290
+ builder.Services.AddDbContext<AppDbContext>();
291
+
292
+ var app = builder.Build();
293
+
294
+ // GET /api/users?skip=0&pageSize=10&sort=name&filter=...
295
+ app.MapGet("/api/users", async (
296
+ AppDbContext db,
297
+ int skip = 0,
298
+ int pageSize = 10,
299
+ string? sort = null,
300
+ string? filter = null) =>
301
+ {
302
+ var query = db.Users.AsQueryable();
303
+
304
+ // Apply filtering
305
+ if (!string.IsNullOrEmpty(filter))
306
+ {
307
+ query = query.Where(u => u.Name.Contains(filter) || u.Email.Contains(filter));
308
+ }
309
+
310
+ // Apply sorting
311
+ if (!string.IsNullOrEmpty(sort))
312
+ {
313
+ query = sort.EndsWith(" desc")
314
+ ? query.OrderByDescending(u => EF.Property<object>(u, sort.Replace(" desc", "")))
315
+ : query.OrderBy(u => EF.Property<object>(u, sort));
316
+ }
317
+
318
+ // Get total count before pagination
319
+ var total = await query.CountAsync();
320
+
321
+ // Apply pagination
322
+ var data = await query.Skip(skip).Take(pageSize).ToListAsync();
323
+
324
+ return Results.Ok(new { data, total });
325
+ });
326
+
327
+ app.Run();
328
+ ```
329
+
330
+ **ASP.NET Core Controller:**
331
+
332
+ ```csharp
333
+ [ApiController]
334
+ [Route("api/[controller]")]
335
+ public class UsersController : ControllerBase
336
+ {
337
+ private readonly AppDbContext _context;
338
+
339
+ public UsersController(AppDbContext context)
340
+ {
341
+ _context = context;
342
+ }
343
+
344
+ [HttpGet]
345
+ public async Task<IActionResult> GetUsers(
346
+ int skip = 0,
347
+ int pageSize = 10,
348
+ string? sort = null,
349
+ string? filter = null)
350
+ {
351
+ var query = _context.Users.AsQueryable();
352
+
353
+ // Apply filtering
354
+ if (!string.IsNullOrEmpty(filter))
355
+ {
356
+ query = query.Where(u =>
357
+ u.Name.Contains(filter) ||
358
+ u.Email.Contains(filter));
359
+ }
360
+
361
+ // Apply sorting
362
+ if (!string.IsNullOrEmpty(sort))
363
+ {
364
+ var descending = sort.EndsWith(" desc");
365
+ var field = sort.Replace(" desc", "").Replace(" asc", "");
366
+
367
+ query = descending
368
+ ? query.OrderByDescending(u => EF.Property<object>(u, field))
369
+ : query.OrderBy(u => EF.Property<object>(u, field));
370
+ }
371
+
372
+ // Get total count
373
+ var total = await query.CountAsync();
374
+
375
+ // Apply pagination
376
+ var data = await query
377
+ .Skip(skip)
378
+ .Take(pageSize)
379
+ .ToListAsync();
380
+
381
+ return Ok(new { data, total });
382
+ }
383
+ }
384
+ ```
385
+
386
+ **Page-based pagination example:**
387
+
388
+ ```csharp
389
+ // GET /api/users?page=1&limit=10
390
+ app.MapGet("/api/users", async (
391
+ AppDbContext db,
392
+ int page = 1,
393
+ int limit = 10) =>
394
+ {
395
+ var total = await db.Users.CountAsync();
396
+ var data = await db.Users
397
+ .Skip((page - 1) * limit)
398
+ .Take(limit)
399
+ .ToListAsync();
400
+
401
+ return Results.Ok(new { data, total });
402
+ });
403
+ ```
404
+
405
+ ```typescript
406
+ // Angular - use page-based pagination
407
+ this.restAdapter = new RestAdapter<User>(
408
+ this.http,
409
+ 'http://localhost:5000/api/users',
410
+ {
411
+ usePagination: 'page',
412
+ pageParam: 'page',
413
+ pageSizeParam: 'limit'
414
+ }
415
+ );
416
+ ```
417
+
418
+ #### RestAdapterConfig Options
419
+
420
+ | Option | Type | Default | Description |
421
+ |--------|------|---------|-------------|
422
+ | `usePagination` | `'page' \| 'skip'` | `'skip'` | Pagination style |
423
+ | `pageParam` | `string` | `'page'` | Query param for page number |
424
+ | `pageSizeParam` | `string` | `'pageSize'` | Query param for page size |
425
+ | `skipParam` | `string` | `'skip'` | Query param for skip count |
426
+ | `sortParam` | `string` | `'sort'` | Query param for sorting |
427
+ | `filterParam` | `string` | `'filter'` | Query param for filtering |
428
+ | `selectParam` | `string` | `'fields'` | Query param for field selection |
429
+ | `dataKey` | `string` | `'data'` | Response property for data array (supports nested paths) |
430
+ | `totalKey` | `string` | `'total'` | Response property for total count (supports nested paths) |
431
+ | `responseMapper` | `function` | - | Custom function to map response to `{ data, total }` |
432
+
433
+ ### Theme Support
434
+
435
+ ```html
436
+ <!-- Light Theme -->
437
+ <nlabs-data-grid [theme]="'light'" />
438
+
439
+ <!-- Dark Theme -->
440
+ <nlabs-data-grid [theme]="'dark'" />
441
+
442
+ <!-- With Theme Selector -->
443
+ <nlabs-data-grid
444
+ [theme]="'dark'"
445
+ [showThemeSelector]="true"
446
+ />
447
+ ```
448
+
449
+ ## Configuration Options
450
+
451
+ ### GridConfig Interface
452
+
453
+ ```typescript
454
+ interface GridConfig {
455
+ // Column definitions
456
+ columns: GridColumn[];
457
+
458
+ // Pagination
459
+ pageSize?: number;
460
+ pageSizeOptions?: number[];
461
+
462
+ // Features
463
+ sortable?: boolean;
464
+ filterable?: boolean;
465
+ selectable?: boolean;
466
+ multiSelect?: boolean;
467
+ resizable?: boolean;
468
+ reorderable?: boolean;
469
+
470
+ // Checkbox column
471
+ showCheckboxColumn?: boolean;
472
+ checkboxColumnWidth?: string;
473
+
474
+ // Actions column
475
+ showActions?: boolean;
476
+ actionsHeader?: string;
477
+ actionsWidth?: string;
478
+
479
+ // UI
480
+ showHeader?: boolean;
481
+ showFooter?: boolean;
482
+ emptyMessage?: string;
483
+ rowHeight?: string;
484
+ }
485
+ ```
486
+
487
+ ### GridColumn Interface
488
+
489
+ ```typescript
490
+ interface GridColumn {
491
+ field: string;
492
+ header: string;
493
+ type?: 'string' | 'number' | 'boolean' | 'date';
494
+ width?: string;
495
+ minWidth?: string;
496
+ maxWidth?: string;
497
+ sortable?: boolean;
498
+ filterable?: boolean;
499
+ resizable?: boolean;
500
+ visible?: boolean;
501
+ format?: (value: any) => string;
502
+ cellTemplate?: TemplateRef<any>;
503
+ }
504
+ ```
505
+
506
+ ## Input Properties
507
+
508
+ | Property | Type | Default | Description |
509
+ |----------|------|---------|-------------|
510
+ | `config` | `GridConfig` | - | Grid configuration object |
511
+ | `adapter` | `IDataAdapter` | - | Data adapter (OData, Mock, etc.) |
512
+ | `data` | `T[]` | `[]` | Static data array |
513
+ | `totalRecords` | `number` | `0` | Total record count for pagination |
514
+ | `autoLoad` | `boolean` | `true` | Auto-load data on init |
515
+ | `lazy` | `boolean` | `true` | Enable lazy loading |
516
+ | `theme` | `'light' \| 'dark'` | `'light'` | Theme mode |
517
+ | `showThemeSelector` | `boolean` | `false` | Show theme toggle button |
518
+ | `showColumnChooser` | `boolean` | `true` | Show column visibility selector |
519
+ | `showGlobalSearch` | `boolean` | `false` | Show global search input |
520
+ | `showAddButton` | `boolean` | `false` | Show add new button |
521
+ | `addButtonText` | `string` | `'Add New'` | Add button text |
522
+ | `showExport` | `boolean` | `false` | Show export buttons |
523
+ | `exportFileName` | `string` | `'export'` | Export file name |
524
+ | `showFooter` | `boolean` | `true` | Show grid footer |
525
+
526
+ ## Output Events
527
+
528
+ | Event | Payload | Description |
529
+ |-------|---------|-------------|
530
+ | `dataLoad` | `GridDataResult<T>` | Fired when data is loaded |
531
+ | `rowSelect` | `T` | Fired when a row is selected |
532
+ | `rowUnselect` | `T` | Fired when a row is unselected |
533
+ | `stateChange` | `GridState` | Fired when grid state changes |
534
+ | `addClick` | `void` | Fired when add button is clicked |
535
+ | `excelExport` | `T[]` | Fired when Excel export is requested |
536
+ | `pdfExport` | `T[]` | Fired when PDF export is requested |
537
+
538
+ ## Styling
539
+
540
+ ### CSS Variables
541
+
542
+ The grid uses CSS variables for theming. You can customize colors by overriding these variables:
543
+
544
+ ```css
545
+ :root {
546
+ --grid-primary-color: #4096ff;
547
+ --grid-primary-hover: #1677ff;
548
+ --grid-bg-primary: #ffffff;
549
+ --grid-bg-secondary: #f5f7fa;
550
+ --grid-bg-hover: #f5f9ff;
551
+ --grid-text-primary: #262626;
552
+ --grid-text-secondary: #666666;
553
+ --grid-border-color: #d9d9d9;
554
+ --grid-border-dark: #bfbfbf;
555
+ --grid-radius-sm: 4px;
556
+ --grid-radius-md: 6px;
557
+ --grid-radius-lg: 8px;
558
+ }
559
+ ```
560
+
561
+ ### Dark Theme Variables
562
+
563
+ ```css
564
+ [data-theme='dark'] {
565
+ --grid-bg-primary: #1f1f1f;
566
+ --grid-bg-secondary: #141414;
567
+ --grid-bg-hover: #2a2a2a;
568
+ --grid-text-primary: #e0e0e0;
569
+ --grid-text-secondary: #a0a0a0;
570
+ --grid-border-color: #404040;
571
+ --grid-border-dark: #4a4a4a;
572
+ }
573
+ ```
574
+
575
+ ### Custom Button Styles
576
+
577
+ ```css
578
+ .btn-edit {
579
+ color: var(--grid-primary-color, #4096ff);
580
+ border: 1px solid var(--grid-primary-color, #4096ff);
581
+ background: var(--grid-bg-primary, #fff);
582
+ }
583
+
584
+ .btn-delete {
585
+ color: #ef4444;
586
+ border: 1px solid #ef4444;
587
+ background: var(--grid-bg-primary, #fff);
588
+ }
589
+ ```
590
+
591
+ ## Browser Support
592
+
593
+ - Chrome (latest)
594
+ - Firefox (latest)
595
+ - Safari (latest)
596
+ - Edge (latest)
597
+
598
+ ## Requirements
599
+
600
+ - Angular 21+
601
+ - TypeScript 5.9+
602
+ - RxJS 7.8+
603
+
604
+ ## Building the Library
605
+
606
+ ```bash
607
+ # Build the library
608
+ ng build nlabs-grid
609
+
610
+ # Watch mode
611
+ ng build nlabs-grid --watch
612
+ ```
613
+
614
+ ## Publishing
615
+
616
+ ```bash
617
+ # Navigate to dist folder
618
+ cd dist/nlabs-grid
619
+
620
+ # Publish to npm
621
+ npm publish
622
+ ```
623
+
624
+ ## License
625
+
626
+ MIT License
627
+
628
+ ## Author
629
+
630
+ nLabs Development Team
631
+
632
+ ## Contributing
633
+
634
+ Contributions are welcome! Please feel free to submit a Pull Request.
635
+
636
+ ## Support
637
+
638
+ For issues and questions, please use the GitHub issue tracker.