ng-adflowtail 0.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 +657 -0
- package/fesm2022/ng-adflowtail-ad-data-table.mjs +389 -0
- package/fesm2022/ng-adflowtail-ad-data-table.mjs.map +1 -0
- package/fesm2022/ng-adflowtail-ad-multi-select.mjs +97 -0
- package/fesm2022/ng-adflowtail-ad-multi-select.mjs.map +1 -0
- package/fesm2022/ng-adflowtail.mjs +40 -0
- package/fesm2022/ng-adflowtail.mjs.map +1 -0
- package/package.json +34 -0
- package/types/ng-adflowtail-ad-data-table.d.ts +139 -0
- package/types/ng-adflowtail-ad-multi-select.d.ts +38 -0
- package/types/ng-adflowtail.d.ts +15 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import * as i2 from '@angular/common';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { EventEmitter, Output, Input, Component } from '@angular/core';
|
|
5
|
+
import * as i3 from '@angular/forms';
|
|
6
|
+
import { FormsModule } from '@angular/forms';
|
|
7
|
+
import { initFlowbite } from 'flowbite';
|
|
8
|
+
import * as i1 from 'ng-adflowtail';
|
|
9
|
+
import { AdPrimaryColorDirective } from 'ng-adflowtail';
|
|
10
|
+
|
|
11
|
+
class AdDataTable {
|
|
12
|
+
title = '';
|
|
13
|
+
data = [];
|
|
14
|
+
tableStyle;
|
|
15
|
+
config = {
|
|
16
|
+
columns: [],
|
|
17
|
+
itemsPerPage: 10,
|
|
18
|
+
showPagination: true,
|
|
19
|
+
showPageSize: true,
|
|
20
|
+
pageSizeOptions: [5, 10, 25, 50],
|
|
21
|
+
showSearch: true,
|
|
22
|
+
searchPlaceholder: 'Search...',
|
|
23
|
+
showActions: false,
|
|
24
|
+
showColumnSearch: false,
|
|
25
|
+
};
|
|
26
|
+
pageChange = new EventEmitter();
|
|
27
|
+
pageSizeChange = new EventEmitter();
|
|
28
|
+
rowClick = new EventEmitter();
|
|
29
|
+
actionClick = new EventEmitter();
|
|
30
|
+
searchChange = new EventEmitter();
|
|
31
|
+
columnSearchChange = new EventEmitter();
|
|
32
|
+
// Pagination properties
|
|
33
|
+
currentPage = 1;
|
|
34
|
+
pageSize = 10;
|
|
35
|
+
totalPages = 1;
|
|
36
|
+
paginatedData = [];
|
|
37
|
+
searchTerm = '';
|
|
38
|
+
// Column search properties
|
|
39
|
+
columnSearchValues = {};
|
|
40
|
+
showColumnFilters = {};
|
|
41
|
+
dateRangeValues = {};
|
|
42
|
+
ngOnInit() {
|
|
43
|
+
this.pageSize = this.config.pageSize || this.config.itemsPerPage || 10;
|
|
44
|
+
this.currentPage = this.config.currentPage || 1;
|
|
45
|
+
this.initializeColumnSearch();
|
|
46
|
+
this.updatePagination();
|
|
47
|
+
}
|
|
48
|
+
ngAfterViewInit() {
|
|
49
|
+
initFlowbite();
|
|
50
|
+
}
|
|
51
|
+
ngOnChanges(changes) {
|
|
52
|
+
if (changes['data']) {
|
|
53
|
+
this.updatePagination();
|
|
54
|
+
}
|
|
55
|
+
if (changes['config']) {
|
|
56
|
+
this.pageSize = this.config.pageSize || this.config.itemsPerPage || 10;
|
|
57
|
+
this.currentPage = this.config.currentPage || 1;
|
|
58
|
+
this.initializeColumnSearch();
|
|
59
|
+
this.updatePagination();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
initializeColumnSearch() {
|
|
63
|
+
// Initialize column search values for searchable columns
|
|
64
|
+
this.config.columns.forEach((column) => {
|
|
65
|
+
if (column.searchable) {
|
|
66
|
+
if (column.searchType === 'dateRange') {
|
|
67
|
+
if (!this.dateRangeValues[column.field]) {
|
|
68
|
+
this.dateRangeValues[column.field] = { start: null, end: null };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// else if (column.searchType === 'multiSelect') {
|
|
72
|
+
// if (!this.multiSelectValues[column.field]) {
|
|
73
|
+
// this.multiSelectValues[column.field] = [];
|
|
74
|
+
// }
|
|
75
|
+
// if (!this.multiSelectDropdownOpen[column.field]) {
|
|
76
|
+
// this.multiSelectDropdownOpen[column.field] = false;
|
|
77
|
+
// }
|
|
78
|
+
// }
|
|
79
|
+
else if (column.searchType === 'autocomplete') {
|
|
80
|
+
if (!this.columnSearchValues[column.field]) {
|
|
81
|
+
this.columnSearchValues[column.field] = '';
|
|
82
|
+
}
|
|
83
|
+
// if (!this.autocompleteSuggestions[column.field]) {
|
|
84
|
+
// this.autocompleteSuggestions[column.field] = [];
|
|
85
|
+
// }
|
|
86
|
+
// if (!this.autocompleteLoading[column.field]) {
|
|
87
|
+
// this.autocompleteLoading[column.field] = false;
|
|
88
|
+
// }
|
|
89
|
+
// if (!this.autocompleteDropdownOpen[column.field]) {
|
|
90
|
+
// this.autocompleteDropdownOpen[column.field] = false;
|
|
91
|
+
// }
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
if (!this.columnSearchValues[column.field]) {
|
|
95
|
+
this.columnSearchValues[column.field] = '';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// Text search - trigger on Enter key
|
|
102
|
+
onTextSearchKeyup(column, event) {
|
|
103
|
+
if (event.key === 'Enter') {
|
|
104
|
+
// const searchTerm = event.target.value;
|
|
105
|
+
this.onSearch(event);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Select search - trigger on change
|
|
109
|
+
onSelectSearchChange(column) {
|
|
110
|
+
const searchValue = this.columnSearchValues[column.field];
|
|
111
|
+
this.currentPage = 1;
|
|
112
|
+
this.updatePagination();
|
|
113
|
+
this.columnSearchChange.emit({
|
|
114
|
+
column: column.header,
|
|
115
|
+
value: searchValue,
|
|
116
|
+
field: column.field,
|
|
117
|
+
searchType: 'select',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Multi-select search - trigger on change
|
|
121
|
+
onMultiSelectSearchChange(column) {
|
|
122
|
+
const selectedValues = this.columnSearchValues[column.field];
|
|
123
|
+
this.currentPage = 1;
|
|
124
|
+
this.updatePagination();
|
|
125
|
+
this.columnSearchChange.emit({
|
|
126
|
+
column: column.header,
|
|
127
|
+
value: selectedValues,
|
|
128
|
+
field: column.field,
|
|
129
|
+
searchType: 'multiSelect',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// Date search - trigger on change
|
|
133
|
+
onDateSearchChange(column) {
|
|
134
|
+
console.log('coming fee');
|
|
135
|
+
const searchValue = this.columnSearchValues[column.field];
|
|
136
|
+
this.currentPage = 1;
|
|
137
|
+
this.updatePagination();
|
|
138
|
+
this.columnSearchChange.emit({
|
|
139
|
+
column: column.header,
|
|
140
|
+
value: searchValue,
|
|
141
|
+
field: column.field,
|
|
142
|
+
searchType: 'date',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Date range search - trigger on change
|
|
146
|
+
onDateRangeSearchChange(column) {
|
|
147
|
+
const dateRange = this.dateRangeValues[column.field];
|
|
148
|
+
this.currentPage = 1;
|
|
149
|
+
this.updatePagination();
|
|
150
|
+
this.columnSearchChange.emit({
|
|
151
|
+
column: column.header,
|
|
152
|
+
value: dateRange,
|
|
153
|
+
field: column.field,
|
|
154
|
+
searchType: 'dateRange',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
updatePagination() {
|
|
158
|
+
// Calculate total pages based on totalCount from input
|
|
159
|
+
const totalItems = this.config.totalCount ?? this.data.length;
|
|
160
|
+
this.totalPages = Math.ceil(totalItems / this.pageSize);
|
|
161
|
+
// Ensure current page is valid
|
|
162
|
+
if (this.currentPage > this.totalPages && this.totalPages > 0) {
|
|
163
|
+
this.currentPage = Math.max(1, this.totalPages);
|
|
164
|
+
}
|
|
165
|
+
this.paginatedData = this.data;
|
|
166
|
+
}
|
|
167
|
+
getNestedValue(obj, path) {
|
|
168
|
+
return path.split('.').reduce((current, key) => current?.[key], obj);
|
|
169
|
+
}
|
|
170
|
+
formatValue(item, column) {
|
|
171
|
+
const value = this.getNestedValue(item, column.field);
|
|
172
|
+
if (value === null || value === undefined)
|
|
173
|
+
return '-';
|
|
174
|
+
switch (column.type) {
|
|
175
|
+
case 'currency':
|
|
176
|
+
return new Intl.NumberFormat('en-US', {
|
|
177
|
+
style: 'currency',
|
|
178
|
+
currency: 'SAR',
|
|
179
|
+
minimumFractionDigits: 2,
|
|
180
|
+
maximumFractionDigits: 2,
|
|
181
|
+
}).format(value);
|
|
182
|
+
case 'date':
|
|
183
|
+
const date = new Date(value);
|
|
184
|
+
return date.toLocaleDateString('en-US', {
|
|
185
|
+
year: 'numeric',
|
|
186
|
+
month: 'short',
|
|
187
|
+
day: 'numeric',
|
|
188
|
+
});
|
|
189
|
+
case 'status':
|
|
190
|
+
const statusClass = column.statusColors?.[value] || 'bg-gray-100 text-gray-800';
|
|
191
|
+
return `<span class="px-2 py-1 rounded-full text-xs font-medium ${statusClass}">${value}</span>`;
|
|
192
|
+
case 'action':
|
|
193
|
+
return column.actionTemplate ? column.actionTemplate(item) : value;
|
|
194
|
+
case 'template':
|
|
195
|
+
return column.actionTemplate ? column.actionTemplate(item) : value;
|
|
196
|
+
default:
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
onSearch(event) {
|
|
201
|
+
this.searchTerm = event.target.value;
|
|
202
|
+
this.currentPage = 1;
|
|
203
|
+
this.searchChange.emit(this.searchTerm);
|
|
204
|
+
}
|
|
205
|
+
onColumnSearchSubmit(column) {
|
|
206
|
+
const searchValue = this.columnSearchValues[column.field] || '';
|
|
207
|
+
this.currentPage = 1;
|
|
208
|
+
this.updatePagination();
|
|
209
|
+
// Emit column search event
|
|
210
|
+
this.columnSearchChange.emit({
|
|
211
|
+
column: column.header,
|
|
212
|
+
value: searchValue,
|
|
213
|
+
field: column.field,
|
|
214
|
+
searchType: column.searchType || 'text',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
onColumnSearchClear(column) {
|
|
218
|
+
this.columnSearchValues[column.field] = '';
|
|
219
|
+
this.currentPage = 1;
|
|
220
|
+
this.updatePagination();
|
|
221
|
+
// Emit clear search event
|
|
222
|
+
this.columnSearchChange.emit({
|
|
223
|
+
column: column.header,
|
|
224
|
+
value: '',
|
|
225
|
+
field: column.field,
|
|
226
|
+
searchType: '',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
toggleColumnFilter(columnField) {
|
|
230
|
+
this.showColumnFilters[columnField] = !this.showColumnFilters[columnField];
|
|
231
|
+
}
|
|
232
|
+
onPageChange(page) {
|
|
233
|
+
if (page >= 1 && page <= this.totalPages) {
|
|
234
|
+
this.currentPage = page;
|
|
235
|
+
this.pageChange.emit(page);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
onPageSizeChange(event) {
|
|
239
|
+
this.pageSize = parseInt(event.target.value, 10);
|
|
240
|
+
this.pageSizeChange.emit(this.pageSize);
|
|
241
|
+
}
|
|
242
|
+
onRowClick(item) {
|
|
243
|
+
this.rowClick.emit(item);
|
|
244
|
+
}
|
|
245
|
+
// Handle action click - now checks disabled state
|
|
246
|
+
onActionClick(actionId, item, event) {
|
|
247
|
+
event.stopPropagation();
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
// Find the action configuration
|
|
250
|
+
let action;
|
|
251
|
+
for (const column of this.config.columns) {
|
|
252
|
+
if (column.type === 'action' && column.actions) {
|
|
253
|
+
action = column.actions.find((a) => a.id === actionId);
|
|
254
|
+
if (action)
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// If action not found or is disabled, do nothing
|
|
259
|
+
if (!action || this.isActionDisabled(action, item)) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// Emit the action event to parent component
|
|
263
|
+
this.actionClick.emit({
|
|
264
|
+
action: actionId,
|
|
265
|
+
item: item,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
getPageNumbers() {
|
|
269
|
+
const pages = [];
|
|
270
|
+
const maxVisible = 5;
|
|
271
|
+
if (this.totalPages <= maxVisible) {
|
|
272
|
+
for (let i = 1; i <= this.totalPages; i++) {
|
|
273
|
+
pages.push(i);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
let start = Math.max(1, this.currentPage - 2);
|
|
278
|
+
let end = Math.min(this.totalPages, start + maxVisible - 1);
|
|
279
|
+
if (end - start < maxVisible - 1) {
|
|
280
|
+
start = Math.max(1, end - maxVisible + 1);
|
|
281
|
+
}
|
|
282
|
+
for (let i = start; i <= end; i++) {
|
|
283
|
+
pages.push(i);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return pages;
|
|
287
|
+
}
|
|
288
|
+
getStartIndex() {
|
|
289
|
+
const totalItems = this.config.totalCount ?? this.data.length;
|
|
290
|
+
if (totalItems === 0)
|
|
291
|
+
return 0;
|
|
292
|
+
return (this.currentPage - 1) * this.pageSize + 1;
|
|
293
|
+
}
|
|
294
|
+
getEndIndex() {
|
|
295
|
+
const totalItems = this.config.totalCount ?? this.data.length;
|
|
296
|
+
if (totalItems === 0)
|
|
297
|
+
return 0;
|
|
298
|
+
const endIndex = this.currentPage * this.pageSize;
|
|
299
|
+
return Math.min(endIndex, totalItems);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Get color classes for action button
|
|
303
|
+
*/
|
|
304
|
+
getActionColorClass(action) {
|
|
305
|
+
const baseClasses = 'font-medium rounded transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
|
|
306
|
+
// If custom className is provided, use it
|
|
307
|
+
if (action.className) {
|
|
308
|
+
return `${baseClasses} ${action.className}`;
|
|
309
|
+
}
|
|
310
|
+
// Color mapping
|
|
311
|
+
const colorMap = {
|
|
312
|
+
primary: 'text-fg-brand hover:underline',
|
|
313
|
+
success: 'text-fg-success hover:underline',
|
|
314
|
+
danger: 'text-fg-danger hover:underline',
|
|
315
|
+
warning: 'text-fg-warning hover:underline',
|
|
316
|
+
};
|
|
317
|
+
const colorClass = colorMap[action.color || 'primary'];
|
|
318
|
+
return `${baseClasses} ${colorClass}`;
|
|
319
|
+
}
|
|
320
|
+
// Get actions for a specific item (with visibility check)
|
|
321
|
+
getActionsForItem(column, item) {
|
|
322
|
+
if (!column.actions)
|
|
323
|
+
return [];
|
|
324
|
+
return column.actions.filter((action) => {
|
|
325
|
+
// Check if action should be visible
|
|
326
|
+
if (action.visible) {
|
|
327
|
+
try {
|
|
328
|
+
return action.visible(item);
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
console.error(`Error in visible check for action ${action.id}:`, error);
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return true; // Show by default
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// Check if action is disabled
|
|
339
|
+
isActionDisabled(action, item) {
|
|
340
|
+
if (action.disabled) {
|
|
341
|
+
try {
|
|
342
|
+
return action.disabled(item);
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
console.error(`Error in disabled check for action ${action.id}:`, error);
|
|
346
|
+
return true; // Disable on error for safety
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AdDataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
352
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: AdDataTable, isStandalone: true, selector: "ad-data-table", inputs: { title: "title", data: "data", tableStyle: "tableStyle", config: "config" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", rowClick: "rowClick", actionClick: "actionClick", searchChange: "searchChange", columnSearchChange: "columnSearchChange" }, usesOnChanges: true, hostDirectives: [{ directive: i1.AdPrimaryColorDirective, inputs: ["primaryColor", "primaryColor"] }], ngImport: i0, template: "<div class=\"space-y-4\">\n <!-- Search and Page Size Controls -->\n @if (title.length > 0) {\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6\">\n <h2 class=\"text-2xl font-bold\">{{ title }}</h2>\n </div>\n }\n @if (config.showColumnSearch) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 mb-6\">\n @for (column of config.columns; track column) {\n @if (column.searchable) {\n <div class=\"relative\">\n <label for=\"input-group-1\" class=\"block mb-2.5 text-sm font-medium text-heading\">{{\n column.header\n }}</label>\n <!-- Text Input Search -->\n @if (column.searchType === 'text' || !column.searchType) {\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-gray-400\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <input\n type=\"text\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n [placeholder]=\"column.searchPlaceholder || 'Search ' + column.header + '...'\"\n (keyup.enter)=\"onColumnSearchSubmit(column)\"\n id=\"input-group-1\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n placeholder=\"name@flowbite.com\"\n />\n </div>\n }\n\n <!-- Select Dropdown Search -->\n @if (column.searchType === 'select') {\n <div class=\"flex gap-2\">\n <select\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onSelectSearchChange(column)\"\n class=\"block w-full px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n >\n <option value=\"\">All {{ column.header }}</option>\n @for (option of column.searchOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Multi Select Dropdown Search -->\n @if (column.searchType === 'multiSelect') {\n <div class=\"flex gap-2\">\n <select\n multiple\n size=\"1\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onMultiSelectSearchChange(column)\"\n class=\"block w-full bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 py-2.5 shadow-xs placeholder:text-body\"\n >\n <option value=\"\">All {{ column.header }}</option>\n @for (option of column.searchOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Date Search -->\n @if (column.searchType === 'date') {\n <input\n type=\"date\"\n format=\"mm/dd/yyyy\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onDateSearchChange(column)\"\n class=\"custom-date-input block w-full pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date\"\n />\n }\n\n <!-- Date Range Search -->\n @if (column.searchType === 'dateRange') {\n <div id=\"date-range-picker\" date-rangepicker class=\"flex items-center\">\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-body\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Zm3-7h.01v.01H8V13Zm4 0h.01v.01H12V13Zm4 0h.01v.01H16V13Zm-8 4h.01v.01H8V17Zm4 0h.01v.01H12V17Zm4 0h.01v.01H16V17Z\"\n />\n </svg>\n </div>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n [(ngModel)]=\"dateRangeValues[column.field].start\"\n (change)=\"onDateRangeSearchChange(column)\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date start\"\n />\n </div>\n <span class=\"mx-4 text-body\">to</span>\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-body\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Zm3-7h.01v.01H8V13Zm4 0h.01v.01H12V13Zm4 0h.01v.01H16V13Zm-8 4h.01v.01H8V17Zm4 0h.01v.01H12V17Zm4 0h.01v.01H16V17Z\"\n />\n </svg>\n </div>\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n [(ngModel)]=\"dateRangeValues[column.field].end\"\n (change)=\"onDateRangeSearchChange(column)\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date end\"\n />\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Table -->\n <div class=\"relative overflow-x-auto bg-neutral-primary-soft\" [ngClass]=\"tableStyle\">\n <table class=\"w-full text-sm text-left rtl:text-right text-body\">\n <thead class=\"bg-neutral-secondary-soft border-b border-default sticky top-0\">\n <tr>\n @for (column of config.columns; track column) {\n <th scope=\"col\" class=\"px-6 py-4 font-medium text-heading\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n <!-- No Data Row -->\n @if (paginatedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"config.columns.length\" class=\"px-6 py-12 text-center text-gray-500\">\n <div class=\"flex flex-col items-center gap-2\">\n <svg\n class=\"w-12 h-12 text-gray-300\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path\n d=\"M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4\"\n stroke-width=\"1.5\"\n />\n </svg>\n <span class=\"text-base font-medium\">No data available</span>\n <span class=\"text-sm\"\n >Try adjusting your search or filter to find what you're looking for.</span\n >\n </div>\n </td>\n </tr>\n } @else {\n <!-- Data Rows -->\n @for (item of paginatedData; track $index) {\n <tr\n class=\"odd:bg-neutral-primary even:bg-neutral-secondary-soft border-b border-default\"\n (click)=\"onRowClick(item)\"\n >\n @for (column of config.columns; track $index) {\n <td class=\"px-6 py-4\">\n <!-- Status Badge -->\n @if (column.type === 'status') {\n <div [innerHTML]=\"formatValue(item, column)\"></div>\n }\n\n <!-- Action Button -->\n @if (column.type === 'action') {\n <div class=\"flex items-center gap-2 flex-wrap\">\n <!-- Multiple Actions -->\n @if (column.actions && column.actions.length > 0) {\n @for (action of getActionsForItem(column, item); track action.id) {\n <button\n (click)=\"onActionClick(action.id, item, $event)\"\n [disabled]=\"isActionDisabled(action, item)\"\n [title]=\"action.tooltip || action.label\"\n [ngClass]=\"getActionColorClass(action)\"\n [class.opacity-50]=\"isActionDisabled(action, item)\"\n [class.cursor-not-allowed]=\"isActionDisabled(action, item)\"\n [class.cursor-pointer]=\"!isActionDisabled(action, item)\"\n >\n <!-- @if (getActionIcon(action)) {\n <span class=\"text-base\">{{ getActionIcon(action) }}</span>\n } -->\n @if (action.label) {\n <span>{{ action.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n\n @if (column.type === 'template') {\n <div>\n @if (column.actionTemplate) {\n <div class=\"flex\">\n <div [innerHTML]=\"formatValue(item, column)\"></div>\n </div>\n }\n </div>\n }\n\n <!-- Regular Text -->\n @if (\n !column.type ||\n column.type === 'text' ||\n column.type === 'number' ||\n column.type === 'currency'\n ) {\n <div>\n <span\n [class.font-medium]=\"$index === 0 && column.field === 'id'\"\n [class.text-heading]=\"$index === 0 && column.field === 'id'\"\n >\n {{ formatValue(item, column) }}\n </span>\n </div>\n }\n\n <!-- Date -->\n @if (column.type === 'date') {\n <div>\n {{ formatValue(item, column) }}\n </div>\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n @if (config.showPagination && (config.totalCount ?? data.length) > 0) {\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <!-- Info Text -->\n <div class=\"text-sm text-gray-500\">\n Showing <span class=\"font-medium\">{{ getStartIndex() }}</span> to\n <span class=\"font-medium\">{{ getEndIndex() }}</span> of\n <span class=\"font-medium\">{{ config.totalCount ?? data.length }}</span> entries\n </div>\n\n @if (config.showPageSize) {\n <div class=\"flex items-center gap-3 text-sm\">\n <span class=\"text-gray-500\">Show</span>\n <select\n (change)=\"onPageSizeChange($event)\"\n class=\"block w-14 px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n >\n @for (size of config.pageSizeOptions; track $index) {\n <option [value]=\"size\" [selected]=\"size === pageSize\">\n {{ size }}\n </option>\n }\n </select>\n <span class=\"text-gray-500\">entries</span>\n </div>\n }\n\n <!-- Pagination Controls -->\n <ul class=\"flex -space-x-px text-sm\">\n <!-- Previous Button -->\n <li>\n <button\n (click)=\"onPageChange(currentPage - 1)\"\n [disabled]=\"currentPage === 1\"\n class=\"flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-s-base text-sm px-3 h-9 focus:outline-none\"\n >\n Previous\n </button>\n </li>\n\n <!-- Page Numbers -->\n @for (page of getPageNumbers(); track $index) {\n <li>\n <button\n (click)=\"onPageChange(page)\"\n class=\"flex items-center justify-center box-border border border-default-medium font-medium text-sm w-9 h-9 focus:outline-none\"\n [ngClass]=\"{\n ' text-fg-brand bg-neutral-tertiary-medium hover:text-fg-brand':\n page === currentPage,\n ' text-body bg-neutral-secondary-medium hover:bg-neutral-tertiary-medium hover:text-heading':\n page !== currentPage,\n }\"\n >\n {{ page }}\n </button>\n </li>\n }\n\n <!-- Next Button -->\n <li>\n <button\n (click)=\"onPageChange(currentPage + 1)\"\n [disabled]=\"currentPage === totalPages\"\n class=\"flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-e-base text-sm px-3 h-9 focus:outline-none\"\n >\n Next\n </button>\n </li>\n </ul>\n </div>\n }\n</div>\n", styles: [".custom-date-wrapper{position:relative;display:inline-block;width:100%}input[type=date]{position:relative;appearance:none;-webkit-appearance:none}input[type=date]::-webkit-calendar-picker-indicator{position:absolute;right:12px;top:50%;transform:translateY(-50%);width:20px;height:20px;padding:0;cursor:pointer;background-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\" /></svg>');background-size:18px;background-position:center;background-repeat:no-repeat;opacity:.6;transition:all .2s ease}input[type=date]::-webkit-calendar-picker-indicator:hover{opacity:1;transform:translateY(-50%) scale(1.1);background-color:#0000000d;border-radius:4px}input[type=date]:before{content:attr(placeholder);position:absolute;left:12px;top:50%;transform:translateY(-50%);color:#9ca3af;pointer-events:none}input[type=date]:valid:before{display:none}input[type=date]::-webkit-datetime-edit{padding:0 8px;font-family:inherit}input[type=date]::-webkit-datetime-edit-month-field,input[type=date]::-webkit-datetime-edit-day-field,input[type=date]::-webkit-datetime-edit-year-field{padding:4px 2px;border-radius:3px}input[type=date]::-webkit-datetime-edit-month-field:focus,input[type=date]::-webkit-datetime-edit-day-field:focus,input[type=date]::-webkit-datetime-edit-year-field:focus{background-color:#e0e7ff;color:#1e40af;outline:none}input[type=date]::-webkit-datetime-edit-text{padding:0 2px;color:#6b7280}input[type=date]:disabled{background-color:#f3f4f6;cursor:not-allowed;opacity:.6}input[type=date]:disabled::-webkit-calendar-picker-indicator{cursor:not-allowed;opacity:.3}input[type=date]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}input[type=date]{-moz-appearance:textfield}input[type=date]::-webkit-inner-spin-button,input[type=date]::-webkit-outer-spin-button{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.SelectMultipleControlValueAccessor, selector: "select[multiple]:not([ngNoCva])[formControlName],select[multiple]:not([ngNoCva])[formControl],select[multiple]:not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
353
|
+
}
|
|
354
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AdDataTable, decorators: [{
|
|
355
|
+
type: Component,
|
|
356
|
+
args: [{ selector: 'ad-data-table', imports: [CommonModule, FormsModule], hostDirectives: [
|
|
357
|
+
{
|
|
358
|
+
directive: AdPrimaryColorDirective,
|
|
359
|
+
inputs: ['primaryColor']
|
|
360
|
+
}
|
|
361
|
+
], template: "<div class=\"space-y-4\">\n <!-- Search and Page Size Controls -->\n @if (title.length > 0) {\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6\">\n <h2 class=\"text-2xl font-bold\">{{ title }}</h2>\n </div>\n }\n @if (config.showColumnSearch) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 mb-6\">\n @for (column of config.columns; track column) {\n @if (column.searchable) {\n <div class=\"relative\">\n <label for=\"input-group-1\" class=\"block mb-2.5 text-sm font-medium text-heading\">{{\n column.header\n }}</label>\n <!-- Text Input Search -->\n @if (column.searchType === 'text' || !column.searchType) {\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-gray-400\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <input\n type=\"text\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n [placeholder]=\"column.searchPlaceholder || 'Search ' + column.header + '...'\"\n (keyup.enter)=\"onColumnSearchSubmit(column)\"\n id=\"input-group-1\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n placeholder=\"name@flowbite.com\"\n />\n </div>\n }\n\n <!-- Select Dropdown Search -->\n @if (column.searchType === 'select') {\n <div class=\"flex gap-2\">\n <select\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onSelectSearchChange(column)\"\n class=\"block w-full px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n >\n <option value=\"\">All {{ column.header }}</option>\n @for (option of column.searchOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Multi Select Dropdown Search -->\n @if (column.searchType === 'multiSelect') {\n <div class=\"flex gap-2\">\n <select\n multiple\n size=\"1\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onMultiSelectSearchChange(column)\"\n class=\"block w-full bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 py-2.5 shadow-xs placeholder:text-body\"\n >\n <option value=\"\">All {{ column.header }}</option>\n @for (option of column.searchOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Date Search -->\n @if (column.searchType === 'date') {\n <input\n type=\"date\"\n format=\"mm/dd/yyyy\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onDateSearchChange(column)\"\n class=\"custom-date-input block w-full pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date\"\n />\n }\n\n <!-- Date Range Search -->\n @if (column.searchType === 'dateRange') {\n <div id=\"date-range-picker\" date-rangepicker class=\"flex items-center\">\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-body\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Zm3-7h.01v.01H8V13Zm4 0h.01v.01H12V13Zm4 0h.01v.01H16V13Zm-8 4h.01v.01H8V17Zm4 0h.01v.01H12V17Zm4 0h.01v.01H16V17Z\"\n />\n </svg>\n </div>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n [(ngModel)]=\"dateRangeValues[column.field].start\"\n (change)=\"onDateRangeSearchChange(column)\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date start\"\n />\n </div>\n <span class=\"mx-4 text-body\">to</span>\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-body\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Zm3-7h.01v.01H8V13Zm4 0h.01v.01H12V13Zm4 0h.01v.01H16V13Zm-8 4h.01v.01H8V17Zm4 0h.01v.01H12V17Zm4 0h.01v.01H16V17Z\"\n />\n </svg>\n </div>\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n [(ngModel)]=\"dateRangeValues[column.field].end\"\n (change)=\"onDateRangeSearchChange(column)\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date end\"\n />\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Table -->\n <div class=\"relative overflow-x-auto bg-neutral-primary-soft\" [ngClass]=\"tableStyle\">\n <table class=\"w-full text-sm text-left rtl:text-right text-body\">\n <thead class=\"bg-neutral-secondary-soft border-b border-default sticky top-0\">\n <tr>\n @for (column of config.columns; track column) {\n <th scope=\"col\" class=\"px-6 py-4 font-medium text-heading\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n <!-- No Data Row -->\n @if (paginatedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"config.columns.length\" class=\"px-6 py-12 text-center text-gray-500\">\n <div class=\"flex flex-col items-center gap-2\">\n <svg\n class=\"w-12 h-12 text-gray-300\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path\n d=\"M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4\"\n stroke-width=\"1.5\"\n />\n </svg>\n <span class=\"text-base font-medium\">No data available</span>\n <span class=\"text-sm\"\n >Try adjusting your search or filter to find what you're looking for.</span\n >\n </div>\n </td>\n </tr>\n } @else {\n <!-- Data Rows -->\n @for (item of paginatedData; track $index) {\n <tr\n class=\"odd:bg-neutral-primary even:bg-neutral-secondary-soft border-b border-default\"\n (click)=\"onRowClick(item)\"\n >\n @for (column of config.columns; track $index) {\n <td class=\"px-6 py-4\">\n <!-- Status Badge -->\n @if (column.type === 'status') {\n <div [innerHTML]=\"formatValue(item, column)\"></div>\n }\n\n <!-- Action Button -->\n @if (column.type === 'action') {\n <div class=\"flex items-center gap-2 flex-wrap\">\n <!-- Multiple Actions -->\n @if (column.actions && column.actions.length > 0) {\n @for (action of getActionsForItem(column, item); track action.id) {\n <button\n (click)=\"onActionClick(action.id, item, $event)\"\n [disabled]=\"isActionDisabled(action, item)\"\n [title]=\"action.tooltip || action.label\"\n [ngClass]=\"getActionColorClass(action)\"\n [class.opacity-50]=\"isActionDisabled(action, item)\"\n [class.cursor-not-allowed]=\"isActionDisabled(action, item)\"\n [class.cursor-pointer]=\"!isActionDisabled(action, item)\"\n >\n <!-- @if (getActionIcon(action)) {\n <span class=\"text-base\">{{ getActionIcon(action) }}</span>\n } -->\n @if (action.label) {\n <span>{{ action.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n\n @if (column.type === 'template') {\n <div>\n @if (column.actionTemplate) {\n <div class=\"flex\">\n <div [innerHTML]=\"formatValue(item, column)\"></div>\n </div>\n }\n </div>\n }\n\n <!-- Regular Text -->\n @if (\n !column.type ||\n column.type === 'text' ||\n column.type === 'number' ||\n column.type === 'currency'\n ) {\n <div>\n <span\n [class.font-medium]=\"$index === 0 && column.field === 'id'\"\n [class.text-heading]=\"$index === 0 && column.field === 'id'\"\n >\n {{ formatValue(item, column) }}\n </span>\n </div>\n }\n\n <!-- Date -->\n @if (column.type === 'date') {\n <div>\n {{ formatValue(item, column) }}\n </div>\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n @if (config.showPagination && (config.totalCount ?? data.length) > 0) {\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <!-- Info Text -->\n <div class=\"text-sm text-gray-500\">\n Showing <span class=\"font-medium\">{{ getStartIndex() }}</span> to\n <span class=\"font-medium\">{{ getEndIndex() }}</span> of\n <span class=\"font-medium\">{{ config.totalCount ?? data.length }}</span> entries\n </div>\n\n @if (config.showPageSize) {\n <div class=\"flex items-center gap-3 text-sm\">\n <span class=\"text-gray-500\">Show</span>\n <select\n (change)=\"onPageSizeChange($event)\"\n class=\"block w-14 px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n >\n @for (size of config.pageSizeOptions; track $index) {\n <option [value]=\"size\" [selected]=\"size === pageSize\">\n {{ size }}\n </option>\n }\n </select>\n <span class=\"text-gray-500\">entries</span>\n </div>\n }\n\n <!-- Pagination Controls -->\n <ul class=\"flex -space-x-px text-sm\">\n <!-- Previous Button -->\n <li>\n <button\n (click)=\"onPageChange(currentPage - 1)\"\n [disabled]=\"currentPage === 1\"\n class=\"flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-s-base text-sm px-3 h-9 focus:outline-none\"\n >\n Previous\n </button>\n </li>\n\n <!-- Page Numbers -->\n @for (page of getPageNumbers(); track $index) {\n <li>\n <button\n (click)=\"onPageChange(page)\"\n class=\"flex items-center justify-center box-border border border-default-medium font-medium text-sm w-9 h-9 focus:outline-none\"\n [ngClass]=\"{\n ' text-fg-brand bg-neutral-tertiary-medium hover:text-fg-brand':\n page === currentPage,\n ' text-body bg-neutral-secondary-medium hover:bg-neutral-tertiary-medium hover:text-heading':\n page !== currentPage,\n }\"\n >\n {{ page }}\n </button>\n </li>\n }\n\n <!-- Next Button -->\n <li>\n <button\n (click)=\"onPageChange(currentPage + 1)\"\n [disabled]=\"currentPage === totalPages\"\n class=\"flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-e-base text-sm px-3 h-9 focus:outline-none\"\n >\n Next\n </button>\n </li>\n </ul>\n </div>\n }\n</div>\n", styles: [".custom-date-wrapper{position:relative;display:inline-block;width:100%}input[type=date]{position:relative;appearance:none;-webkit-appearance:none}input[type=date]::-webkit-calendar-picker-indicator{position:absolute;right:12px;top:50%;transform:translateY(-50%);width:20px;height:20px;padding:0;cursor:pointer;background-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\" /></svg>');background-size:18px;background-position:center;background-repeat:no-repeat;opacity:.6;transition:all .2s ease}input[type=date]::-webkit-calendar-picker-indicator:hover{opacity:1;transform:translateY(-50%) scale(1.1);background-color:#0000000d;border-radius:4px}input[type=date]:before{content:attr(placeholder);position:absolute;left:12px;top:50%;transform:translateY(-50%);color:#9ca3af;pointer-events:none}input[type=date]:valid:before{display:none}input[type=date]::-webkit-datetime-edit{padding:0 8px;font-family:inherit}input[type=date]::-webkit-datetime-edit-month-field,input[type=date]::-webkit-datetime-edit-day-field,input[type=date]::-webkit-datetime-edit-year-field{padding:4px 2px;border-radius:3px}input[type=date]::-webkit-datetime-edit-month-field:focus,input[type=date]::-webkit-datetime-edit-day-field:focus,input[type=date]::-webkit-datetime-edit-year-field:focus{background-color:#e0e7ff;color:#1e40af;outline:none}input[type=date]::-webkit-datetime-edit-text{padding:0 2px;color:#6b7280}input[type=date]:disabled{background-color:#f3f4f6;cursor:not-allowed;opacity:.6}input[type=date]:disabled::-webkit-calendar-picker-indicator{cursor:not-allowed;opacity:.3}input[type=date]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}input[type=date]{-moz-appearance:textfield}input[type=date]::-webkit-inner-spin-button,input[type=date]::-webkit-outer-spin-button{display:none}\n"] }]
|
|
362
|
+
}], propDecorators: { title: [{
|
|
363
|
+
type: Input
|
|
364
|
+
}], data: [{
|
|
365
|
+
type: Input
|
|
366
|
+
}], tableStyle: [{
|
|
367
|
+
type: Input
|
|
368
|
+
}], config: [{
|
|
369
|
+
type: Input
|
|
370
|
+
}], pageChange: [{
|
|
371
|
+
type: Output
|
|
372
|
+
}], pageSizeChange: [{
|
|
373
|
+
type: Output
|
|
374
|
+
}], rowClick: [{
|
|
375
|
+
type: Output
|
|
376
|
+
}], actionClick: [{
|
|
377
|
+
type: Output
|
|
378
|
+
}], searchChange: [{
|
|
379
|
+
type: Output
|
|
380
|
+
}], columnSearchChange: [{
|
|
381
|
+
type: Output
|
|
382
|
+
}] } });
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Generated bundle index. Do not edit.
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
export { AdDataTable };
|
|
389
|
+
//# sourceMappingURL=ng-adflowtail-ad-data-table.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-adflowtail-ad-data-table.mjs","sources":["../../../projects/ng-adflowtail/ad-data-table/src/lib/ad-data-table.ts","../../../projects/ng-adflowtail/ad-data-table/src/lib/ad-data-table.html","../../../projects/ng-adflowtail/ad-data-table/src/ng-adflowtail-ad-data-table.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n AfterViewInit,\n Component,\n EventEmitter,\n Input,\n OnChanges,\n OnInit,\n Output,\n SimpleChanges,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { initFlowbite } from 'flowbite';\nimport { AdPrimaryColorDirective } from 'ng-adflowtail';\n\nexport type DataTableButtonAction = 'EDIT' | 'DELETE' | 'VIEW';\n\nexport interface TableColumn {\n field: string;\n header: string;\n type?: 'text' | 'number' | 'date' | 'currency' | 'status' | 'action' | 'template';\n format?: string;\n actionTemplate?: (item: any) => string;\n statusColors?: { [key: string]: string };\n searchable?: boolean; // New property to enable search for specific column\n searchPlaceholder?: string; // Custom placeholder for column search\n searchType?: 'text' | 'select' | 'multiSelect' | 'date' | 'dateRange' | 'number' | 'autocomplete';\n searchOptions?: Array<{ value: any; label: string }>;\n multiSelectConfig?: {\n maxItems?: number;\n showSelectAll?: boolean;\n };\n autocompleteConfig?: {\n apiUrl?: string;\n debounceTime?: number;\n minChars?: number;\n valueField?: string;\n labelField?: string;\n customSearchFn?: (searchTerm: string) => Promise<Array<{ value: any; label: string }>>;\n };\n actions?: TableAction[]; // New: Multiple actions\n\n}\n\nexport interface TableAction {\n id: DataTableButtonAction;\n label: string;\n icon?: string;\n color?: 'primary' | 'success' | 'danger' | 'warning' | 'info' | 'secondary';\n className?: string;\n visible?: (item: any) => boolean;\n disabled?: (item: any) => boolean;\n tooltip?: string;\n}\n\nexport interface TableConfig {\n columns: TableColumn[];\n totalCount?: number;\n currentPage?: number;\n pageSize?: number;\n itemsPerPage?: number;\n showPagination?: boolean;\n showPageSize?: boolean;\n pageSizeOptions?: number[];\n showSearch?: boolean;\n searchPlaceholder?: string;\n showActions?: boolean;\n showColumnSearch?: boolean; // Enable/disable column-specific search\n}\n\nexport interface ColumnSearchEvent {\n column: string;\n value: any;\n field: string;\n searchType: string;\n}\n\nexport interface DateRangeValue {\n start: Date | string | null;\n end: Date | string | null;\n}\n\nexport interface SuggestionItem {\n value: any;\n label: string;\n}\n\n@Component({\n selector: 'ad-data-table',\n imports: [CommonModule, FormsModule],\n templateUrl: './ad-data-table.html',\n styleUrl: './ad-data-table.css',\n hostDirectives: [\n {\n directive: AdPrimaryColorDirective,\n inputs: ['primaryColor']\n }\n ]\n})\nexport class AdDataTable implements OnInit, OnChanges, AfterViewInit {\n @Input() title: string = '';\n @Input() data: any[] = [];\n @Input() tableStyle?: string;\n @Input() config: TableConfig = {\n columns: [],\n itemsPerPage: 10,\n showPagination: true,\n showPageSize: true,\n pageSizeOptions: [5, 10, 25, 50],\n showSearch: true,\n searchPlaceholder: 'Search...',\n showActions: false,\n showColumnSearch: false,\n };\n\n @Output() pageChange = new EventEmitter<number>();\n @Output() pageSizeChange = new EventEmitter<number>();\n @Output() rowClick = new EventEmitter<any>();\n @Output() actionClick = new EventEmitter<{ action: DataTableButtonAction; item: any }>();\n @Output() searchChange = new EventEmitter<string>();\n @Output() columnSearchChange = new EventEmitter<ColumnSearchEvent>();\n\n // Pagination properties\n currentPage: number = 1;\n pageSize: number = 10;\n totalPages: number = 1;\n paginatedData: any[] = [];\n searchTerm: string = '';\n\n // Column search properties\n columnSearchValues: { [key: string]: string } = {};\n showColumnFilters: { [key: string]: boolean } = {};\n dateRangeValues: { [key: string]: DateRangeValue } = {};\n\n ngOnInit() {\n this.pageSize = this.config.pageSize || this.config.itemsPerPage || 10;\n this.currentPage = this.config.currentPage || 1;\n this.initializeColumnSearch();\n this.updatePagination();\n }\n\n ngAfterViewInit(): void {\n initFlowbite();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes['data']) {\n this.updatePagination();\n }\n\n if (changes['config']) {\n this.pageSize = this.config.pageSize || this.config.itemsPerPage || 10;\n this.currentPage = this.config.currentPage || 1;\n this.initializeColumnSearch();\n this.updatePagination();\n }\n }\n\n initializeColumnSearch() {\n // Initialize column search values for searchable columns\n this.config.columns.forEach((column) => {\n if (column.searchable) {\n if (column.searchType === 'dateRange') {\n if (!this.dateRangeValues[column.field]) {\n this.dateRangeValues[column.field] = { start: null, end: null };\n }\n }\n // else if (column.searchType === 'multiSelect') {\n // if (!this.multiSelectValues[column.field]) {\n // this.multiSelectValues[column.field] = [];\n // }\n // if (!this.multiSelectDropdownOpen[column.field]) {\n // this.multiSelectDropdownOpen[column.field] = false;\n // }\n // }\n else if (column.searchType === 'autocomplete') {\n if (!this.columnSearchValues[column.field]) {\n this.columnSearchValues[column.field] = '';\n }\n // if (!this.autocompleteSuggestions[column.field]) {\n // this.autocompleteSuggestions[column.field] = [];\n // }\n // if (!this.autocompleteLoading[column.field]) {\n // this.autocompleteLoading[column.field] = false;\n // }\n // if (!this.autocompleteDropdownOpen[column.field]) {\n // this.autocompleteDropdownOpen[column.field] = false;\n // }\n } else {\n if (!this.columnSearchValues[column.field]) {\n this.columnSearchValues[column.field] = '';\n }\n }\n }\n });\n }\n\n // Text search - trigger on Enter key\n onTextSearchKeyup(column: TableColumn, event: any) {\n if (event.key === 'Enter') {\n // const searchTerm = event.target.value;\n this.onSearch(event);\n }\n }\n\n // Select search - trigger on change\n onSelectSearchChange(column: TableColumn) {\n const searchValue = this.columnSearchValues[column.field];\n this.currentPage = 1;\n this.updatePagination();\n\n this.columnSearchChange.emit({\n column: column.header,\n value: searchValue,\n field: column.field,\n searchType: 'select',\n });\n }\n\n // Multi-select search - trigger on change\n onMultiSelectSearchChange(column: TableColumn) {\n const selectedValues = this.columnSearchValues[column.field];\n this.currentPage = 1;\n this.updatePagination();\n\n this.columnSearchChange.emit({\n column: column.header,\n value: selectedValues,\n field: column.field,\n searchType: 'multiSelect',\n });\n }\n\n // Date search - trigger on change\n onDateSearchChange(column: TableColumn) {\n console.log('coming fee');\n const searchValue = this.columnSearchValues[column.field];\n this.currentPage = 1;\n this.updatePagination();\n\n this.columnSearchChange.emit({\n column: column.header,\n value: searchValue,\n field: column.field,\n searchType: 'date',\n });\n }\n\n // Date range search - trigger on change\n onDateRangeSearchChange(column: TableColumn) {\n const dateRange = this.dateRangeValues[column.field];\n this.currentPage = 1;\n this.updatePagination();\n\n this.columnSearchChange.emit({\n column: column.header,\n value: dateRange,\n field: column.field,\n searchType: 'dateRange',\n });\n }\n\n updatePagination() {\n // Calculate total pages based on totalCount from input\n const totalItems = this.config.totalCount ?? this.data.length;\n this.totalPages = Math.ceil(totalItems / this.pageSize);\n\n // Ensure current page is valid\n if (this.currentPage > this.totalPages && this.totalPages > 0) {\n this.currentPage = Math.max(1, this.totalPages);\n }\n\n this.paginatedData = this.data;\n }\n\n getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current, key) => current?.[key], obj);\n }\n\n formatValue(item: any, column: TableColumn): any {\n const value = this.getNestedValue(item, column.field);\n\n if (value === null || value === undefined) return '-';\n\n switch (column.type) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'SAR',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(value);\n\n case 'date':\n const date = new Date(value);\n return date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n\n case 'status':\n const statusClass = column.statusColors?.[value] || 'bg-gray-100 text-gray-800';\n return `<span class=\"px-2 py-1 rounded-full text-xs font-medium ${statusClass}\">${value}</span>`;\n\n case 'action':\n return column.actionTemplate ? column.actionTemplate(item) : value;\n\n case 'template':\n return column.actionTemplate ? column.actionTemplate(item) : value;\n\n default:\n return value;\n }\n }\n\n onSearch(event: any) {\n this.searchTerm = event.target.value;\n this.currentPage = 1;\n this.searchChange.emit(this.searchTerm);\n }\n\n onColumnSearchSubmit(column: TableColumn) {\n const searchValue = this.columnSearchValues[column.field] || '';\n this.currentPage = 1;\n this.updatePagination();\n\n // Emit column search event\n this.columnSearchChange.emit({\n column: column.header,\n value: searchValue,\n field: column.field,\n searchType: column.searchType || 'text',\n });\n }\n\n onColumnSearchClear(column: TableColumn) {\n this.columnSearchValues[column.field] = '';\n this.currentPage = 1;\n this.updatePagination();\n\n // Emit clear search event\n this.columnSearchChange.emit({\n column: column.header,\n value: '',\n field: column.field,\n searchType: '',\n });\n }\n\n toggleColumnFilter(columnField: string) {\n this.showColumnFilters[columnField] = !this.showColumnFilters[columnField];\n }\n\n onPageChange(page: number) {\n if (page >= 1 && page <= this.totalPages) {\n this.currentPage = page;\n this.pageChange.emit(page);\n }\n }\n\n onPageSizeChange(event: any) {\n this.pageSize = parseInt(event.target.value, 10);\n this.pageSizeChange.emit(this.pageSize);\n }\n\n onRowClick(item: any) {\n this.rowClick.emit(item);\n }\n\n // Handle action click - now checks disabled state\n onActionClick(actionId: DataTableButtonAction, item: any, event: Event) {\n event.stopPropagation();\n event.preventDefault();\n\n // Find the action configuration\n let action: TableAction | undefined;\n for (const column of this.config.columns) {\n if (column.type === 'action' && column.actions) {\n action = column.actions.find((a) => a.id === actionId);\n if (action) break;\n }\n }\n\n // If action not found or is disabled, do nothing\n if (!action || this.isActionDisabled(action, item)) {\n return;\n }\n\n // Emit the action event to parent component\n this.actionClick.emit({\n action: actionId,\n item: item,\n });\n }\n getPageNumbers(): number[] {\n const pages: number[] = [];\n const maxVisible = 5;\n\n if (this.totalPages <= maxVisible) {\n for (let i = 1; i <= this.totalPages; i++) {\n pages.push(i);\n }\n } else {\n let start = Math.max(1, this.currentPage - 2);\n let end = Math.min(this.totalPages, start + maxVisible - 1);\n\n if (end - start < maxVisible - 1) {\n start = Math.max(1, end - maxVisible + 1);\n }\n\n for (let i = start; i <= end; i++) {\n pages.push(i);\n }\n }\n\n return pages;\n }\n\n getStartIndex(): number {\n const totalItems = this.config.totalCount ?? this.data.length;\n if (totalItems === 0) return 0;\n return (this.currentPage - 1) * this.pageSize + 1;\n }\n\n getEndIndex(): number {\n const totalItems = this.config.totalCount ?? this.data.length;\n if (totalItems === 0) return 0;\n\n const endIndex = this.currentPage * this.pageSize;\n return Math.min(endIndex, totalItems);\n }\n\n /**\n * Get color classes for action button\n */\n getActionColorClass(action: TableAction): string {\n const baseClasses =\n 'font-medium rounded transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';\n\n // If custom className is provided, use it\n if (action.className) {\n return `${baseClasses} ${action.className}`;\n }\n\n // Color mapping\n const colorMap: { [key: string]: string } = {\n primary: 'text-fg-brand hover:underline',\n success: 'text-fg-success hover:underline',\n danger: 'text-fg-danger hover:underline',\n warning: 'text-fg-warning hover:underline',\n };\n\n const colorClass = colorMap[action.color || 'primary'];\n return `${baseClasses} ${colorClass}`;\n }\n\n // Get actions for a specific item (with visibility check)\n getActionsForItem(column: TableColumn, item: any): TableAction[] {\n if (!column.actions) return [];\n\n return column.actions.filter((action) => {\n // Check if action should be visible\n if (action.visible) {\n try {\n return action.visible(item);\n } catch (error) {\n console.error(`Error in visible check for action ${action.id}:`, error);\n return false;\n }\n }\n return true; // Show by default\n });\n }\n\n // Check if action is disabled\n isActionDisabled(action: TableAction, item: any): boolean {\n if (action.disabled) {\n try {\n return action.disabled(item);\n } catch (error) {\n console.error(`Error in disabled check for action ${action.id}:`, error);\n return true; // Disable on error for safety\n }\n }\n return false;\n }\n}\n","<div class=\"space-y-4\">\n <!-- Search and Page Size Controls -->\n @if (title.length > 0) {\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6\">\n <h2 class=\"text-2xl font-bold\">{{ title }}</h2>\n </div>\n }\n @if (config.showColumnSearch) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 mb-6\">\n @for (column of config.columns; track column) {\n @if (column.searchable) {\n <div class=\"relative\">\n <label for=\"input-group-1\" class=\"block mb-2.5 text-sm font-medium text-heading\">{{\n column.header\n }}</label>\n <!-- Text Input Search -->\n @if (column.searchType === 'text' || !column.searchType) {\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-gray-400\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <input\n type=\"text\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n [placeholder]=\"column.searchPlaceholder || 'Search ' + column.header + '...'\"\n (keyup.enter)=\"onColumnSearchSubmit(column)\"\n id=\"input-group-1\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n placeholder=\"name@flowbite.com\"\n />\n </div>\n }\n\n <!-- Select Dropdown Search -->\n @if (column.searchType === 'select') {\n <div class=\"flex gap-2\">\n <select\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onSelectSearchChange(column)\"\n class=\"block w-full px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n >\n <option value=\"\">All {{ column.header }}</option>\n @for (option of column.searchOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Multi Select Dropdown Search -->\n @if (column.searchType === 'multiSelect') {\n <div class=\"flex gap-2\">\n <select\n multiple\n size=\"1\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onMultiSelectSearchChange(column)\"\n class=\"block w-full bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 py-2.5 shadow-xs placeholder:text-body\"\n >\n <option value=\"\">All {{ column.header }}</option>\n @for (option of column.searchOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Date Search -->\n @if (column.searchType === 'date') {\n <input\n type=\"date\"\n format=\"mm/dd/yyyy\"\n [(ngModel)]=\"columnSearchValues[column.field]\"\n (change)=\"onDateSearchChange(column)\"\n class=\"custom-date-input block w-full pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date\"\n />\n }\n\n <!-- Date Range Search -->\n @if (column.searchType === 'dateRange') {\n <div id=\"date-range-picker\" date-rangepicker class=\"flex items-center\">\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-body\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Zm3-7h.01v.01H8V13Zm4 0h.01v.01H12V13Zm4 0h.01v.01H16V13Zm-8 4h.01v.01H8V17Zm4 0h.01v.01H12V17Zm4 0h.01v.01H16V17Z\"\n />\n </svg>\n </div>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n [(ngModel)]=\"dateRangeValues[column.field].start\"\n (change)=\"onDateRangeSearchChange(column)\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date start\"\n />\n </div>\n <span class=\"mx-4 text-body\">to</span>\n <div class=\"relative\">\n <div\n class=\"absolute inset-y-0 inset-s-0 flex items-center ps-3 pointer-events-none\"\n >\n <svg\n class=\"w-4 h-4 text-body\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Zm3-7h.01v.01H8V13Zm4 0h.01v.01H12V13Zm4 0h.01v.01H16V13Zm-8 4h.01v.01H8V17Zm4 0h.01v.01H12V17Zm4 0h.01v.01H16V17Z\"\n />\n </svg>\n </div>\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n [(ngModel)]=\"dateRangeValues[column.field].end\"\n (change)=\"onDateRangeSearchChange(column)\"\n class=\"block w-full ps-9 pe-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand px-3 shadow-xs placeholder:text-body\"\n placeholder=\"Select date end\"\n />\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Table -->\n <div class=\"relative overflow-x-auto bg-neutral-primary-soft\" [ngClass]=\"tableStyle\">\n <table class=\"w-full text-sm text-left rtl:text-right text-body\">\n <thead class=\"bg-neutral-secondary-soft border-b border-default sticky top-0\">\n <tr>\n @for (column of config.columns; track column) {\n <th scope=\"col\" class=\"px-6 py-4 font-medium text-heading\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n <!-- No Data Row -->\n @if (paginatedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"config.columns.length\" class=\"px-6 py-12 text-center text-gray-500\">\n <div class=\"flex flex-col items-center gap-2\">\n <svg\n class=\"w-12 h-12 text-gray-300\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path\n d=\"M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4\"\n stroke-width=\"1.5\"\n />\n </svg>\n <span class=\"text-base font-medium\">No data available</span>\n <span class=\"text-sm\"\n >Try adjusting your search or filter to find what you're looking for.</span\n >\n </div>\n </td>\n </tr>\n } @else {\n <!-- Data Rows -->\n @for (item of paginatedData; track $index) {\n <tr\n class=\"odd:bg-neutral-primary even:bg-neutral-secondary-soft border-b border-default\"\n (click)=\"onRowClick(item)\"\n >\n @for (column of config.columns; track $index) {\n <td class=\"px-6 py-4\">\n <!-- Status Badge -->\n @if (column.type === 'status') {\n <div [innerHTML]=\"formatValue(item, column)\"></div>\n }\n\n <!-- Action Button -->\n @if (column.type === 'action') {\n <div class=\"flex items-center gap-2 flex-wrap\">\n <!-- Multiple Actions -->\n @if (column.actions && column.actions.length > 0) {\n @for (action of getActionsForItem(column, item); track action.id) {\n <button\n (click)=\"onActionClick(action.id, item, $event)\"\n [disabled]=\"isActionDisabled(action, item)\"\n [title]=\"action.tooltip || action.label\"\n [ngClass]=\"getActionColorClass(action)\"\n [class.opacity-50]=\"isActionDisabled(action, item)\"\n [class.cursor-not-allowed]=\"isActionDisabled(action, item)\"\n [class.cursor-pointer]=\"!isActionDisabled(action, item)\"\n >\n <!-- @if (getActionIcon(action)) {\n <span class=\"text-base\">{{ getActionIcon(action) }}</span>\n } -->\n @if (action.label) {\n <span>{{ action.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n\n @if (column.type === 'template') {\n <div>\n @if (column.actionTemplate) {\n <div class=\"flex\">\n <div [innerHTML]=\"formatValue(item, column)\"></div>\n </div>\n }\n </div>\n }\n\n <!-- Regular Text -->\n @if (\n !column.type ||\n column.type === 'text' ||\n column.type === 'number' ||\n column.type === 'currency'\n ) {\n <div>\n <span\n [class.font-medium]=\"$index === 0 && column.field === 'id'\"\n [class.text-heading]=\"$index === 0 && column.field === 'id'\"\n >\n {{ formatValue(item, column) }}\n </span>\n </div>\n }\n\n <!-- Date -->\n @if (column.type === 'date') {\n <div>\n {{ formatValue(item, column) }}\n </div>\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n @if (config.showPagination && (config.totalCount ?? data.length) > 0) {\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <!-- Info Text -->\n <div class=\"text-sm text-gray-500\">\n Showing <span class=\"font-medium\">{{ getStartIndex() }}</span> to\n <span class=\"font-medium\">{{ getEndIndex() }}</span> of\n <span class=\"font-medium\">{{ config.totalCount ?? data.length }}</span> entries\n </div>\n\n @if (config.showPageSize) {\n <div class=\"flex items-center gap-3 text-sm\">\n <span class=\"text-gray-500\">Show</span>\n <select\n (change)=\"onPageSizeChange($event)\"\n class=\"block w-14 px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand shadow-xs placeholder:text-body\"\n >\n @for (size of config.pageSizeOptions; track $index) {\n <option [value]=\"size\" [selected]=\"size === pageSize\">\n {{ size }}\n </option>\n }\n </select>\n <span class=\"text-gray-500\">entries</span>\n </div>\n }\n\n <!-- Pagination Controls -->\n <ul class=\"flex -space-x-px text-sm\">\n <!-- Previous Button -->\n <li>\n <button\n (click)=\"onPageChange(currentPage - 1)\"\n [disabled]=\"currentPage === 1\"\n class=\"flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-s-base text-sm px-3 h-9 focus:outline-none\"\n >\n Previous\n </button>\n </li>\n\n <!-- Page Numbers -->\n @for (page of getPageNumbers(); track $index) {\n <li>\n <button\n (click)=\"onPageChange(page)\"\n class=\"flex items-center justify-center box-border border border-default-medium font-medium text-sm w-9 h-9 focus:outline-none\"\n [ngClass]=\"{\n ' text-fg-brand bg-neutral-tertiary-medium hover:text-fg-brand':\n page === currentPage,\n ' text-body bg-neutral-secondary-medium hover:bg-neutral-tertiary-medium hover:text-heading':\n page !== currentPage,\n }\"\n >\n {{ page }}\n </button>\n </li>\n }\n\n <!-- Next Button -->\n <li>\n <button\n (click)=\"onPageChange(currentPage + 1)\"\n [disabled]=\"currentPage === totalPages\"\n class=\"flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-e-base text-sm px-3 h-9 focus:outline-none\"\n >\n Next\n </button>\n </li>\n </ul>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;;MAmGa,WAAW,CAAA;IACb,KAAK,GAAW,EAAE;IAClB,IAAI,GAAU,EAAE;AAChB,IAAA,UAAU;AACV,IAAA,MAAM,GAAgB;AAC7B,QAAA,OAAO,EAAE,EAAE;AACX,QAAA,YAAY,EAAE,EAAE;AAChB,QAAA,cAAc,EAAE,IAAI;AACpB,QAAA,YAAY,EAAE,IAAI;QAClB,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAChC,QAAA,UAAU,EAAE,IAAI;AAChB,QAAA,iBAAiB,EAAE,WAAW;AAC9B,QAAA,WAAW,EAAE,KAAK;AAClB,QAAA,gBAAgB,EAAE,KAAK;KACxB;AAES,IAAA,UAAU,GAAG,IAAI,YAAY,EAAU;AACvC,IAAA,cAAc,GAAG,IAAI,YAAY,EAAU;AAC3C,IAAA,QAAQ,GAAG,IAAI,YAAY,EAAO;AAClC,IAAA,WAAW,GAAG,IAAI,YAAY,EAAgD;AAC9E,IAAA,YAAY,GAAG,IAAI,YAAY,EAAU;AACzC,IAAA,kBAAkB,GAAG,IAAI,YAAY,EAAqB;;IAGpE,WAAW,GAAW,CAAC;IACvB,QAAQ,GAAW,EAAE;IACrB,UAAU,GAAW,CAAC;IACtB,aAAa,GAAU,EAAE;IACzB,UAAU,GAAW,EAAE;;IAGvB,kBAAkB,GAA8B,EAAE;IAClD,iBAAiB,GAA+B,EAAE;IAClD,eAAe,GAAsC,EAAE;IAEvD,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE;QACtE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC;QAC/C,IAAI,CAAC,sBAAsB,EAAE;QAC7B,IAAI,CAAC,gBAAgB,EAAE;IACzB;IAEA,eAAe,GAAA;AACb,QAAA,YAAY,EAAE;IAChB;AAEA,IAAA,WAAW,CAAC,OAAsB,EAAA;AAChC,QAAA,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE;YACnB,IAAI,CAAC,gBAAgB,EAAE;QACzB;AAEA,QAAA,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE;AACrB,YAAA,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE;YACtE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC;YAC/C,IAAI,CAAC,sBAAsB,EAAE;YAC7B,IAAI,CAAC,gBAAgB,EAAE;QACzB;IACF;IAEA,sBAAsB,GAAA;;QAEpB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,KAAI;AACrC,YAAA,IAAI,MAAM,CAAC,UAAU,EAAE;AACrB,gBAAA,IAAI,MAAM,CAAC,UAAU,KAAK,WAAW,EAAE;oBACrC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;AACvC,wBAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;oBACjE;gBACF;;;;;;;;;AASK,qBAAA,IAAI,MAAM,CAAC,UAAU,KAAK,cAAc,EAAE;oBAC7C,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;wBAC1C,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;oBAC5C;;;;;;;;;;gBAUF;qBAAO;oBACL,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;wBAC1C,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;oBAC5C;gBACF;YACF;AACF,QAAA,CAAC,CAAC;IACJ;;IAGA,iBAAiB,CAAC,MAAmB,EAAE,KAAU,EAAA;AAC/C,QAAA,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,EAAE;;AAEzB,YAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtB;IACF;;AAGA,IAAA,oBAAoB,CAAC,MAAmB,EAAA;QACtC,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC;AACzD,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE;AAEvB,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,YAAA,UAAU,EAAE,QAAQ;AACrB,SAAA,CAAC;IACJ;;AAGA,IAAA,yBAAyB,CAAC,MAAmB,EAAA;QAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC;AAC5D,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE;AAEvB,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,cAAc;YACrB,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,YAAA,UAAU,EAAE,aAAa;AAC1B,SAAA,CAAC;IACJ;;AAGA,IAAA,kBAAkB,CAAC,MAAmB,EAAA;AACpC,QAAA,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACzB,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC;AACzD,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE;AAEvB,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,YAAA,UAAU,EAAE,MAAM;AACnB,SAAA,CAAC;IACJ;;AAGA,IAAA,uBAAuB,CAAC,MAAmB,EAAA;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC;AACpD,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE;AAEvB,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,YAAA,UAAU,EAAE,WAAW;AACxB,SAAA,CAAC;IACJ;IAEA,gBAAgB,GAAA;;AAEd,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;AAC7D,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC;;AAGvD,QAAA,IAAI,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,EAAE;AAC7D,YAAA,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC;QACjD;AAEA,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI;IAChC;IAEA,cAAc,CAAC,GAAQ,EAAE,IAAY,EAAA;QACnC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC;IACtE;IAEA,WAAW,CAAC,IAAS,EAAE,MAAmB,EAAA;AACxC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC;AAErD,QAAA,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;AAAE,YAAA,OAAO,GAAG;AAErD,QAAA,QAAQ,MAAM,CAAC,IAAI;AACjB,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;AACpC,oBAAA,KAAK,EAAE,UAAU;AACjB,oBAAA,QAAQ,EAAE,KAAK;AACf,oBAAA,qBAAqB,EAAE,CAAC;AACxB,oBAAA,qBAAqB,EAAE,CAAC;AACzB,iBAAA,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;AAElB,YAAA,KAAK,MAAM;AACT,gBAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;AAC5B,gBAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE;AACtC,oBAAA,IAAI,EAAE,SAAS;AACf,oBAAA,KAAK,EAAE,OAAO;AACd,oBAAA,GAAG,EAAE,SAAS;AACf,iBAAA,CAAC;AAEJ,YAAA,KAAK,QAAQ;gBACX,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,GAAG,KAAK,CAAC,IAAI,2BAA2B;AAC/E,gBAAA,OAAO,CAAA,wDAAA,EAA2D,WAAW,CAAA,EAAA,EAAK,KAAK,SAAS;AAElG,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,KAAK;AAEpE,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,KAAK;AAEpE,YAAA;AACE,gBAAA,OAAO,KAAK;;IAElB;AAEA,IAAA,QAAQ,CAAC,KAAU,EAAA;QACjB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK;AACpC,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;IACzC;AAEA,IAAA,oBAAoB,CAAC,MAAmB,EAAA;AACtC,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE;AAC/D,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE;;AAGvB,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,YAAA,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,MAAM;AACxC,SAAA,CAAC;IACJ;AAEA,IAAA,mBAAmB,CAAC,MAAmB,EAAA;QACrC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;AAC1C,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE;;AAGvB,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,EAAE;YACT,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,YAAA,UAAU,EAAE,EAAE;AACf,SAAA,CAAC;IACJ;AAEA,IAAA,kBAAkB,CAAC,WAAmB,EAAA;AACpC,QAAA,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC;IAC5E;AAEA,IAAA,YAAY,CAAC,IAAY,EAAA;QACvB,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACxC,YAAA,IAAI,CAAC,WAAW,GAAG,IAAI;AACvB,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B;IACF;AAEA,IAAA,gBAAgB,CAAC,KAAU,EAAA;AACzB,QAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;QAChD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;IACzC;AAEA,IAAA,UAAU,CAAC,IAAS,EAAA;AAClB,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;IAC1B;;AAGA,IAAA,aAAa,CAAC,QAA+B,EAAE,IAAS,EAAE,KAAY,EAAA;QACpE,KAAK,CAAC,eAAe,EAAE;QACvB,KAAK,CAAC,cAAc,EAAE;;AAGtB,QAAA,IAAI,MAA+B;QACnC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;YACxC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,EAAE;AAC9C,gBAAA,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC;AACtD,gBAAA,IAAI,MAAM;oBAAE;YACd;QACF;;AAGA,QAAA,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;YAClD;QACF;;AAGA,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACpB,YAAA,MAAM,EAAE,QAAQ;AAChB,YAAA,IAAI,EAAE,IAAI;AACX,SAAA,CAAC;IACJ;IACA,cAAc,GAAA;QACZ,MAAM,KAAK,GAAa,EAAE;QAC1B,MAAM,UAAU,GAAG,CAAC;AAEpB,QAAA,IAAI,IAAI,CAAC,UAAU,IAAI,UAAU,EAAE;AACjC,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE;AACzC,gBAAA,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACf;QACF;aAAO;AACL,YAAA,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;AAC7C,YAAA,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,UAAU,GAAG,CAAC,CAAC;YAE3D,IAAI,GAAG,GAAG,KAAK,GAAG,UAAU,GAAG,CAAC,EAAE;AAChC,gBAAA,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,UAAU,GAAG,CAAC,CAAC;YAC3C;AAEA,YAAA,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,EAAE;AACjC,gBAAA,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACf;QACF;AAEA,QAAA,OAAO,KAAK;IACd;IAEA,aAAa,GAAA;AACX,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;QAC7D,IAAI,UAAU,KAAK,CAAC;AAAE,YAAA,OAAO,CAAC;AAC9B,QAAA,OAAO,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC;IACnD;IAEA,WAAW,GAAA;AACT,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;QAC7D,IAAI,UAAU,KAAK,CAAC;AAAE,YAAA,OAAO,CAAC;QAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ;QACjD,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC;IACvC;AAEA;;AAEG;AACH,IAAA,mBAAmB,CAAC,MAAmB,EAAA;QACrC,MAAM,WAAW,GACf,2FAA2F;;AAG7F,QAAA,IAAI,MAAM,CAAC,SAAS,EAAE;AACpB,YAAA,OAAO,GAAG,WAAW,CAAA,CAAA,EAAI,MAAM,CAAC,SAAS,EAAE;QAC7C;;AAGA,QAAA,MAAM,QAAQ,GAA8B;AAC1C,YAAA,OAAO,EAAE,+BAA+B;AACxC,YAAA,OAAO,EAAE,iCAAiC;AAC1C,YAAA,MAAM,EAAE,gCAAgC;AACxC,YAAA,OAAO,EAAE,iCAAiC;SAC3C;QAED,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,SAAS,CAAC;AACtD,QAAA,OAAO,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,UAAU,EAAE;IACvC;;IAGA,iBAAiB,CAAC,MAAmB,EAAE,IAAS,EAAA;QAC9C,IAAI,CAAC,MAAM,CAAC,OAAO;AAAE,YAAA,OAAO,EAAE;QAE9B,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,KAAI;;AAEtC,YAAA,IAAI,MAAM,CAAC,OAAO,EAAE;AAClB,gBAAA,IAAI;AACF,oBAAA,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC7B;gBAAE,OAAO,KAAK,EAAE;oBACd,OAAO,CAAC,KAAK,CAAC,CAAA,kCAAA,EAAqC,MAAM,CAAC,EAAE,CAAA,CAAA,CAAG,EAAE,KAAK,CAAC;AACvE,oBAAA,OAAO,KAAK;gBACd;YACF;YACA,OAAO,IAAI,CAAC;AACd,QAAA,CAAC,CAAC;IACJ;;IAGA,gBAAgB,CAAC,MAAmB,EAAE,IAAS,EAAA;AAC7C,QAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,YAAA,IAAI;AACF,gBAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC9B;YAAE,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,KAAK,CAAC,CAAA,mCAAA,EAAsC,MAAM,CAAC,EAAE,CAAA,CAAA,CAAG,EAAE,KAAK,CAAC;gBACxE,OAAO,IAAI,CAAC;YACd;QACF;AACA,QAAA,OAAO,KAAK;IACd;uGAnYW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAX,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,WAAW,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,OAAA,EAAA,IAAA,EAAA,MAAA,EAAA,UAAA,EAAA,YAAA,EAAA,MAAA,EAAA,QAAA,EAAA,EAAA,OAAA,EAAA,EAAA,UAAA,EAAA,YAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,WAAA,EAAA,aAAA,EAAA,YAAA,EAAA,cAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,EAAA,aAAA,EAAA,IAAA,EAAA,cAAA,EAAA,CAAA,EAAA,SAAA,EAAA,EAAA,CAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,cAAA,EAAA,cAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECnGxB,4ggBAuWA,EAAA,MAAA,EAAA,CAAA,ugEAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED9QY,YAAY,4HAAE,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,cAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,uBAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,wSAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,0BAAA,EAAA,QAAA,EAAA,0JAAA,EAAA,MAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,kCAAA,EAAA,QAAA,EAAA,wIAAA,EAAA,MAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAUxB,WAAW,EAAA,UAAA,EAAA,CAAA;kBAZvB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,WAChB,CAAC,YAAY,EAAE,WAAW,CAAC,EAAA,cAAA,EAGpB;AACd,wBAAA;AACE,4BAAA,SAAS,EAAE,uBAAuB;4BAClC,MAAM,EAAE,CAAC,cAAc;AACxB;AACF,qBAAA,EAAA,QAAA,EAAA,4ggBAAA,EAAA,MAAA,EAAA,CAAA,ugEAAA,CAAA,EAAA;;sBAGA;;sBACA;;sBACA;;sBACA;;sBAYA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;;AExHH;;AAEG;;;;"}
|