cloud-ide-element 1.1.11 → 1.1.13
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/fesm2022/cloud-ide-element.mjs +1418 -643
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +160 -2
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
11
11
|
import { generateStringFromObject, coreRoutesUrl, cidePath, hostManagerRoutesUrl } from 'cloud-ide-lms-model';
|
|
12
12
|
import * as i2$1 from '@angular/router';
|
|
13
13
|
import { tap, catchError as catchError$1 } from 'rxjs/operators';
|
|
14
|
+
import { trigger, state, transition, style, animate } from '@angular/animations';
|
|
14
15
|
|
|
15
16
|
class CapitalizePipe {
|
|
16
17
|
transform(value, capitalizationMethod) {
|
|
@@ -569,6 +570,21 @@ class CideInputComponent {
|
|
|
569
570
|
// ViewChild for date picker template
|
|
570
571
|
datePickerTemplate;
|
|
571
572
|
datePickerViewRef = null;
|
|
573
|
+
// Time picker properties
|
|
574
|
+
showTimePicker = false;
|
|
575
|
+
timeHours = 1;
|
|
576
|
+
timeMinutes = 30;
|
|
577
|
+
timeSeconds = 30;
|
|
578
|
+
timeFormat = 'AM';
|
|
579
|
+
timePickerPortalId = `time-picker-${this.randomString()}`;
|
|
580
|
+
// ViewChild for time picker template
|
|
581
|
+
timePickerTemplate;
|
|
582
|
+
// DateTime picker properties
|
|
583
|
+
showDateTimePicker = false;
|
|
584
|
+
dateTimeStep = 'date';
|
|
585
|
+
dateTimePickerPortalId = `datetime-picker-${this.randomString()}`;
|
|
586
|
+
// ViewChild for datetime picker template
|
|
587
|
+
dateTimePickerTemplate;
|
|
572
588
|
// ==================================METHODS FOR CUSTOM FORM COMPONENT=============================
|
|
573
589
|
// FOR ANGULAR CALLED BY UI
|
|
574
590
|
/**
|
|
@@ -659,6 +675,10 @@ class CideInputComponent {
|
|
|
659
675
|
ngOnDestroy() {
|
|
660
676
|
// Clean up date picker portal
|
|
661
677
|
this.portalService.destroyPortal(this.datePickerPortalId);
|
|
678
|
+
// Clean up time picker portal
|
|
679
|
+
this.portalService.destroyPortal(this.timePickerPortalId);
|
|
680
|
+
// Clean up datetime picker portal
|
|
681
|
+
this.portalService.destroyPortal(this.dateTimePickerPortalId);
|
|
662
682
|
}
|
|
663
683
|
/** @description custom method run when HTML changes, we call method registerd by angular to detect change */
|
|
664
684
|
upDateValue(value) {
|
|
@@ -714,6 +734,25 @@ class CideInputComponent {
|
|
|
714
734
|
}
|
|
715
735
|
}
|
|
716
736
|
}
|
|
737
|
+
else if (type === 'datetime-local' && typeof value === 'string' && value) {
|
|
738
|
+
// Ensure datetime-local is in YYYY-MM-DDTHH:mm format
|
|
739
|
+
const datetimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/;
|
|
740
|
+
if (datetimeRegex.test(value) && !isNaN(Date.parse(value))) {
|
|
741
|
+
return value; // Already in correct format
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
// Try to parse and format unformatted datetime to YYYY-MM-DDTHH:mm
|
|
745
|
+
const parsedDate = new Date(value);
|
|
746
|
+
if (!isNaN(parsedDate.getTime())) {
|
|
747
|
+
const year = parsedDate.getFullYear();
|
|
748
|
+
const month = String(parsedDate.getMonth() + 1).padStart(2, '0');
|
|
749
|
+
const day = String(parsedDate.getDate()).padStart(2, '0');
|
|
750
|
+
const hours = String(parsedDate.getHours()).padStart(2, '0');
|
|
751
|
+
const minutes = String(parsedDate.getMinutes()).padStart(2, '0');
|
|
752
|
+
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
717
756
|
else if (type === 'url' && typeof value === 'string' && value) {
|
|
718
757
|
// Normalize URL format - ensure it starts with http:// or https://
|
|
719
758
|
if (!value.startsWith('http://') && !value.startsWith('https://')) {
|
|
@@ -786,6 +825,36 @@ class CideInputComponent {
|
|
|
786
825
|
return false;
|
|
787
826
|
}
|
|
788
827
|
}
|
|
828
|
+
else if (type == 'time') {
|
|
829
|
+
if (typeof (value) == 'string') {
|
|
830
|
+
if (value?.length > 0) {
|
|
831
|
+
// Validate time format (HH:mm or HH:mm:ss)
|
|
832
|
+
const timeRegex = /^\d{2}:\d{2}(:\d{2})?$/;
|
|
833
|
+
return timeRegex.test(value);
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
else if (type == 'datetime-local') {
|
|
844
|
+
if (typeof (value) == 'string') {
|
|
845
|
+
if (value?.length > 0) {
|
|
846
|
+
// Validate datetime-local format (YYYY-MM-DDTHH:mm:ss or YYYY-MM-DDTHH:mm)
|
|
847
|
+
const datetimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/;
|
|
848
|
+
return datetimeRegex.test(value) && !isNaN(Date.parse(value));
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
789
858
|
else {
|
|
790
859
|
return false;
|
|
791
860
|
}
|
|
@@ -891,6 +960,45 @@ class CideInputComponent {
|
|
|
891
960
|
validation_status.validation.required = `Date is required!`;
|
|
892
961
|
}
|
|
893
962
|
}
|
|
963
|
+
else if (this.type == 'datetime-local') {
|
|
964
|
+
if (typeof (value) == 'string' && value) {
|
|
965
|
+
// Validate datetime-local format and parse
|
|
966
|
+
const datetimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/;
|
|
967
|
+
if (!datetimeRegex.test(value)) {
|
|
968
|
+
validation_status.status = true;
|
|
969
|
+
validation_status.validation.required = `Invalid datetime format (YYYY-MM-DDTHH:mm)!`;
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
const parsedDate = Date.parse(value);
|
|
973
|
+
if (isNaN(parsedDate)) {
|
|
974
|
+
validation_status.status = true;
|
|
975
|
+
validation_status.validation.required = `Invalid datetime!`;
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
// Validate min datetime
|
|
979
|
+
if (this.min && typeof this.min === 'string') {
|
|
980
|
+
const minDate = Date.parse(this.min);
|
|
981
|
+
if (!isNaN(minDate) && parsedDate < minDate) {
|
|
982
|
+
validation_status.status = true;
|
|
983
|
+
validation_status.validation.required = `Datetime must be after ${this.min}!`;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
// Validate max datetime
|
|
987
|
+
if (this.max && typeof this.max === 'string') {
|
|
988
|
+
const maxDate = Date.parse(this.max);
|
|
989
|
+
if (!isNaN(maxDate) && parsedDate > maxDate) {
|
|
990
|
+
validation_status.status = true;
|
|
991
|
+
validation_status.validation.required = `Datetime must be before ${this.max}!`;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
else if (this.required && !value) {
|
|
998
|
+
validation_status.status = true;
|
|
999
|
+
validation_status.validation.required = `Datetime is required!`;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
894
1002
|
this.isValid = !validation_status.status;
|
|
895
1003
|
this.errorText = Object.values(validation_status.validation).at(0) || '';
|
|
896
1004
|
return validation_status;
|
|
@@ -922,6 +1030,29 @@ class CideInputComponent {
|
|
|
922
1030
|
this.closeDatePicker();
|
|
923
1031
|
}
|
|
924
1032
|
}
|
|
1033
|
+
else if (this.type === 'time') {
|
|
1034
|
+
// Toggle custom time picker using portal service
|
|
1035
|
+
this.showTimePicker = !this.showTimePicker;
|
|
1036
|
+
if (this.showTimePicker) {
|
|
1037
|
+
this.initializeTimePicker();
|
|
1038
|
+
this.createTimePickerUsingPortal();
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
this.closeTimePicker();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
else if (this.type === 'datetime-local') {
|
|
1045
|
+
// Toggle custom datetime picker using portal service
|
|
1046
|
+
this.showDateTimePicker = !this.showDateTimePicker;
|
|
1047
|
+
if (this.showDateTimePicker) {
|
|
1048
|
+
this.dateTimeStep = 'date'; // Start with date selection
|
|
1049
|
+
this.initializeDateTimePicker();
|
|
1050
|
+
this.createDateTimePickerUsingPortal();
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
this.closeDateTimePicker();
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
925
1056
|
else {
|
|
926
1057
|
this.typeInternal = this.type;
|
|
927
1058
|
}
|
|
@@ -963,6 +1094,12 @@ class CideInputComponent {
|
|
|
963
1094
|
this.onValidate();
|
|
964
1095
|
}
|
|
965
1096
|
}
|
|
1097
|
+
/**
|
|
1098
|
+
* @description Helper to check if type is time (for template type narrowing)
|
|
1099
|
+
*/
|
|
1100
|
+
isTimeType() {
|
|
1101
|
+
return this.type === 'time';
|
|
1102
|
+
}
|
|
966
1103
|
/**
|
|
967
1104
|
* @description use to detact the change in type if changes done this method is ued to run
|
|
968
1105
|
*/
|
|
@@ -978,6 +1115,18 @@ class CideInputComponent {
|
|
|
978
1115
|
this.trailingIconInternal = this.trailingIcon || "calendar_today";
|
|
979
1116
|
this.isTrailingIconAllwedClick = true; // Allow clicking calendar icon
|
|
980
1117
|
}
|
|
1118
|
+
else if (this.type === 'time') {
|
|
1119
|
+
this.typeInternal = "time";
|
|
1120
|
+
// Set default schedule icon for time picker
|
|
1121
|
+
this.trailingIconInternal = this.trailingIcon || "schedule";
|
|
1122
|
+
this.isTrailingIconAllwedClick = true; // Allow clicking time icon
|
|
1123
|
+
}
|
|
1124
|
+
else if (this.type === 'datetime-local') {
|
|
1125
|
+
this.typeInternal = "datetime-local";
|
|
1126
|
+
// Set default schedule icon if no trailing icon specified
|
|
1127
|
+
this.trailingIconInternal = this.trailingIcon || "event";
|
|
1128
|
+
this.isTrailingIconAllwedClick = true; // datetime-local now uses custom picker
|
|
1129
|
+
}
|
|
981
1130
|
else if (this.type === 'url') {
|
|
982
1131
|
this.typeInternal = "url";
|
|
983
1132
|
// Set default link icon if no trailing icon specified
|
|
@@ -1190,11 +1339,294 @@ class CideInputComponent {
|
|
|
1190
1339
|
this.ngModel = formattedDate;
|
|
1191
1340
|
this.ngModelChange?.emit(formattedDate);
|
|
1192
1341
|
this.onChange(formattedDate);
|
|
1342
|
+
// Mark as touched and validate
|
|
1343
|
+
this.isTouched = true;
|
|
1344
|
+
this.onTouched();
|
|
1345
|
+
this.isControlValid(formattedDate);
|
|
1193
1346
|
// Regenerate calendar to update selection
|
|
1194
1347
|
this.generateCalendar();
|
|
1195
1348
|
// Close date picker
|
|
1196
1349
|
this.closeDatePicker();
|
|
1197
1350
|
}
|
|
1351
|
+
// ==================== TIME PICKER METHODS ====================
|
|
1352
|
+
/**
|
|
1353
|
+
* @description Initialize time picker with current values
|
|
1354
|
+
*/
|
|
1355
|
+
initializeTimePicker() {
|
|
1356
|
+
if (this.ngModel) {
|
|
1357
|
+
// Parse existing time value (format: HH:mm or HH:mm:ss)
|
|
1358
|
+
const timeString = this.ngModel.toString();
|
|
1359
|
+
const timeParts = timeString.split(':');
|
|
1360
|
+
if (timeParts.length >= 2) {
|
|
1361
|
+
let hours = parseInt(timeParts[0]);
|
|
1362
|
+
const minutes = parseInt(timeParts[1]);
|
|
1363
|
+
const seconds = timeParts[2] ? parseInt(timeParts[2]) : 0;
|
|
1364
|
+
// Convert to 12-hour format
|
|
1365
|
+
this.timeFormat = hours >= 12 ? 'PM' : 'AM';
|
|
1366
|
+
this.timeHours = hours === 0 ? 12 : (hours > 12 ? hours - 12 : hours);
|
|
1367
|
+
this.timeMinutes = minutes;
|
|
1368
|
+
this.timeSeconds = seconds;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
else {
|
|
1372
|
+
// Default to current time
|
|
1373
|
+
const now = new Date();
|
|
1374
|
+
let hours = now.getHours();
|
|
1375
|
+
this.timeFormat = hours >= 12 ? 'PM' : 'AM';
|
|
1376
|
+
this.timeHours = hours === 0 ? 12 : (hours > 12 ? hours - 12 : hours);
|
|
1377
|
+
this.timeMinutes = now.getMinutes();
|
|
1378
|
+
this.timeSeconds = now.getSeconds();
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* @description Create time picker using template-based portal service
|
|
1383
|
+
*/
|
|
1384
|
+
createTimePickerUsingPortal() {
|
|
1385
|
+
const inputElement = document.getElementById(this.id || this.idRandom);
|
|
1386
|
+
if (!inputElement)
|
|
1387
|
+
return;
|
|
1388
|
+
const portalConfig = {
|
|
1389
|
+
template: this.timePickerTemplate,
|
|
1390
|
+
context: { $implicit: this },
|
|
1391
|
+
triggerElement: inputElement,
|
|
1392
|
+
viewContainerRef: this.viewContainerRef,
|
|
1393
|
+
className: 'cide-time-picker-portal',
|
|
1394
|
+
position: 'bottom',
|
|
1395
|
+
align: 'left',
|
|
1396
|
+
offset: { x: 0, y: 8 },
|
|
1397
|
+
closeOnOutsideClick: true,
|
|
1398
|
+
closeOnEscape: true
|
|
1399
|
+
};
|
|
1400
|
+
this.portalService.createTemplatePortal(this.timePickerPortalId, portalConfig);
|
|
1401
|
+
// Register close callback
|
|
1402
|
+
this.portalService.registerCloseCallback(this.timePickerPortalId, () => {
|
|
1403
|
+
this.closeTimePicker();
|
|
1404
|
+
});
|
|
1405
|
+
inputElement.setAttribute('data-portal-trigger', this.timePickerPortalId);
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* @description Close time picker
|
|
1409
|
+
*/
|
|
1410
|
+
closeTimePicker() {
|
|
1411
|
+
this.showTimePicker = false;
|
|
1412
|
+
this.portalService.destroyPortal(this.timePickerPortalId);
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* @description Increment/decrement time value
|
|
1416
|
+
*/
|
|
1417
|
+
adjustTime(unit, direction) {
|
|
1418
|
+
if (unit === 'hours') {
|
|
1419
|
+
if (direction === 'up') {
|
|
1420
|
+
this.timeHours = this.timeHours === 12 ? 1 : this.timeHours + 1;
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
this.timeHours = this.timeHours === 1 ? 12 : this.timeHours - 1;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
else if (unit === 'minutes') {
|
|
1427
|
+
if (direction === 'up') {
|
|
1428
|
+
this.timeMinutes = this.timeMinutes === 59 ? 0 : this.timeMinutes + 1;
|
|
1429
|
+
}
|
|
1430
|
+
else {
|
|
1431
|
+
this.timeMinutes = this.timeMinutes === 0 ? 59 : this.timeMinutes - 1;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
else if (unit === 'seconds') {
|
|
1435
|
+
if (direction === 'up') {
|
|
1436
|
+
this.timeSeconds = this.timeSeconds === 59 ? 0 : this.timeSeconds + 1;
|
|
1437
|
+
}
|
|
1438
|
+
else {
|
|
1439
|
+
this.timeSeconds = this.timeSeconds === 0 ? 59 : this.timeSeconds - 1;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* @description Toggle AM/PM
|
|
1445
|
+
*/
|
|
1446
|
+
toggleTimeFormat() {
|
|
1447
|
+
this.timeFormat = this.timeFormat === 'AM' ? 'PM' : 'AM';
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* @description Apply selected time
|
|
1451
|
+
*/
|
|
1452
|
+
applyTime() {
|
|
1453
|
+
// Convert to 24-hour format
|
|
1454
|
+
let hours = this.timeHours;
|
|
1455
|
+
if (this.timeFormat === 'PM' && hours !== 12) {
|
|
1456
|
+
hours += 12;
|
|
1457
|
+
}
|
|
1458
|
+
else if (this.timeFormat === 'AM' && hours === 12) {
|
|
1459
|
+
hours = 0;
|
|
1460
|
+
}
|
|
1461
|
+
// Format time as HH:mm:ss
|
|
1462
|
+
const timeString = `${hours.toString().padStart(2, '0')}:${this.timeMinutes.toString().padStart(2, '0')}:${this.timeSeconds.toString().padStart(2, '0')}`;
|
|
1463
|
+
this.ngModel = timeString;
|
|
1464
|
+
this.ngModelChange.emit(timeString);
|
|
1465
|
+
this.onChange(timeString);
|
|
1466
|
+
// Mark as touched and validate
|
|
1467
|
+
this.isTouched = true;
|
|
1468
|
+
this.onTouched();
|
|
1469
|
+
this.isControlValid(timeString);
|
|
1470
|
+
this.closeTimePicker();
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* @description Get time display value for the input field
|
|
1474
|
+
*/
|
|
1475
|
+
getTimeDisplayValue() {
|
|
1476
|
+
if (!this.ngModel)
|
|
1477
|
+
return '';
|
|
1478
|
+
const timeString = this.ngModel.toString();
|
|
1479
|
+
const timeParts = timeString.split(':');
|
|
1480
|
+
if (timeParts.length >= 2) {
|
|
1481
|
+
let hours = parseInt(timeParts[0]);
|
|
1482
|
+
const minutes = parseInt(timeParts[1]);
|
|
1483
|
+
const seconds = timeParts[2] ? parseInt(timeParts[2]) : 0;
|
|
1484
|
+
const format = hours >= 12 ? 'PM' : 'AM';
|
|
1485
|
+
hours = hours === 0 ? 12 : (hours > 12 ? hours - 12 : hours);
|
|
1486
|
+
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')} ${format}`;
|
|
1487
|
+
}
|
|
1488
|
+
return timeString;
|
|
1489
|
+
}
|
|
1490
|
+
// ==================== DATETIME PICKER METHODS ====================
|
|
1491
|
+
/**
|
|
1492
|
+
* @description Initialize datetime picker
|
|
1493
|
+
*/
|
|
1494
|
+
initializeDateTimePicker() {
|
|
1495
|
+
if (this.ngModel) {
|
|
1496
|
+
// Parse existing datetime value
|
|
1497
|
+
const dateTimeString = this.ngModel.toString();
|
|
1498
|
+
const dateTime = new Date(dateTimeString);
|
|
1499
|
+
if (!isNaN(dateTime.getTime())) {
|
|
1500
|
+
// Initialize date picker values
|
|
1501
|
+
this.selectedDate = dateTime;
|
|
1502
|
+
this.currentMonth = dateTime.getMonth();
|
|
1503
|
+
this.currentYear = dateTime.getFullYear();
|
|
1504
|
+
this.generateCalendar();
|
|
1505
|
+
// Initialize time picker values
|
|
1506
|
+
let hours = dateTime.getHours();
|
|
1507
|
+
this.timeFormat = hours >= 12 ? 'PM' : 'AM';
|
|
1508
|
+
this.timeHours = hours === 0 ? 12 : (hours > 12 ? hours - 12 : hours);
|
|
1509
|
+
this.timeMinutes = dateTime.getMinutes();
|
|
1510
|
+
this.timeSeconds = dateTime.getSeconds();
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
else {
|
|
1514
|
+
// Default to current date and time
|
|
1515
|
+
const now = new Date();
|
|
1516
|
+
this.selectedDate = now;
|
|
1517
|
+
this.currentMonth = now.getMonth();
|
|
1518
|
+
this.currentYear = now.getFullYear();
|
|
1519
|
+
this.generateCalendar();
|
|
1520
|
+
let hours = now.getHours();
|
|
1521
|
+
this.timeFormat = hours >= 12 ? 'PM' : 'AM';
|
|
1522
|
+
this.timeHours = hours === 0 ? 12 : (hours > 12 ? hours - 12 : hours);
|
|
1523
|
+
this.timeMinutes = now.getMinutes();
|
|
1524
|
+
this.timeSeconds = now.getSeconds();
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* @description Create datetime picker using portal service
|
|
1529
|
+
*/
|
|
1530
|
+
createDateTimePickerUsingPortal() {
|
|
1531
|
+
const inputElement = document.getElementById(this.id || this.idRandom);
|
|
1532
|
+
if (!inputElement)
|
|
1533
|
+
return;
|
|
1534
|
+
// Use combined datetime picker template (shows date and time together)
|
|
1535
|
+
const portalConfig = {
|
|
1536
|
+
template: this.dateTimePickerTemplate,
|
|
1537
|
+
context: { $implicit: this },
|
|
1538
|
+
triggerElement: inputElement,
|
|
1539
|
+
viewContainerRef: this.viewContainerRef,
|
|
1540
|
+
className: 'cide-datetime-picker-portal',
|
|
1541
|
+
position: 'bottom',
|
|
1542
|
+
align: 'left',
|
|
1543
|
+
offset: { x: 0, y: 8 },
|
|
1544
|
+
closeOnOutsideClick: true,
|
|
1545
|
+
closeOnEscape: true
|
|
1546
|
+
};
|
|
1547
|
+
this.portalService.createTemplatePortal(this.dateTimePickerPortalId, portalConfig);
|
|
1548
|
+
this.portalService.registerCloseCallback(this.dateTimePickerPortalId, () => {
|
|
1549
|
+
this.closeDateTimePicker();
|
|
1550
|
+
});
|
|
1551
|
+
inputElement.setAttribute('data-portal-trigger', this.dateTimePickerPortalId);
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* @description Close datetime picker
|
|
1555
|
+
*/
|
|
1556
|
+
closeDateTimePicker() {
|
|
1557
|
+
this.showDateTimePicker = false;
|
|
1558
|
+
this.dateTimeStep = 'date';
|
|
1559
|
+
this.showMonthYearSelector = false;
|
|
1560
|
+
this.portalService.destroyPortal(this.dateTimePickerPortalId);
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* @description Select date in datetime picker (just updates the selected date, time stays visible)
|
|
1564
|
+
*/
|
|
1565
|
+
selectDateTimeDate(dayInfo) {
|
|
1566
|
+
if (!dayInfo.isCurrentMonth)
|
|
1567
|
+
return;
|
|
1568
|
+
// Just update the selected date - time picker remains visible on the right
|
|
1569
|
+
this.selectedDate = dayInfo.date;
|
|
1570
|
+
// Regenerate calendar to update selection
|
|
1571
|
+
this.generateCalendar();
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* @description Apply datetime (combines date and time)
|
|
1575
|
+
*/
|
|
1576
|
+
applyDateTime() {
|
|
1577
|
+
if (!this.selectedDate)
|
|
1578
|
+
return;
|
|
1579
|
+
// Convert time to 24-hour format
|
|
1580
|
+
let hours = this.timeHours;
|
|
1581
|
+
if (this.timeFormat === 'PM' && hours !== 12) {
|
|
1582
|
+
hours += 12;
|
|
1583
|
+
}
|
|
1584
|
+
else if (this.timeFormat === 'AM' && hours === 12) {
|
|
1585
|
+
hours = 0;
|
|
1586
|
+
}
|
|
1587
|
+
// Combine date and time
|
|
1588
|
+
const dateTime = new Date(this.selectedDate);
|
|
1589
|
+
dateTime.setHours(hours, this.timeMinutes, this.timeSeconds);
|
|
1590
|
+
// Format as datetime-local string (YYYY-MM-DDTHH:mm:ss)
|
|
1591
|
+
const dateTimeString = dateTime.toISOString().slice(0, 19);
|
|
1592
|
+
this.ngModel = dateTimeString;
|
|
1593
|
+
this.ngModelChange.emit(dateTimeString);
|
|
1594
|
+
this.onChange(dateTimeString);
|
|
1595
|
+
// Mark as touched and validate
|
|
1596
|
+
this.isTouched = true;
|
|
1597
|
+
this.onTouched();
|
|
1598
|
+
this.isControlValid(dateTimeString);
|
|
1599
|
+
this.closeDateTimePicker();
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* @description Get datetime display value
|
|
1603
|
+
*/
|
|
1604
|
+
getDateTimeDisplayValue() {
|
|
1605
|
+
if (!this.ngModel)
|
|
1606
|
+
return '';
|
|
1607
|
+
const dateTime = new Date(this.ngModel.toString());
|
|
1608
|
+
if (isNaN(dateTime.getTime()))
|
|
1609
|
+
return this.ngModel.toString();
|
|
1610
|
+
// Format: MMM DD, YYYY HH:mm AM/PM
|
|
1611
|
+
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
1612
|
+
const month = monthNames[dateTime.getMonth()];
|
|
1613
|
+
const day = dateTime.getDate();
|
|
1614
|
+
const year = dateTime.getFullYear();
|
|
1615
|
+
let hours = dateTime.getHours();
|
|
1616
|
+
const minutes = dateTime.getMinutes();
|
|
1617
|
+
const format = hours >= 12 ? 'PM' : 'AM';
|
|
1618
|
+
hours = hours === 0 ? 12 : (hours > 12 ? hours - 12 : hours);
|
|
1619
|
+
return `${month} ${day}, ${year} ${hours}:${minutes.toString().padStart(2, '0')} ${format}`;
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* @description Back to date selection in datetime picker
|
|
1623
|
+
*/
|
|
1624
|
+
backToDateSelection() {
|
|
1625
|
+
this.dateTimeStep = 'date';
|
|
1626
|
+
// Recreate portal with date picker template
|
|
1627
|
+
this.portalService.destroyPortal(this.dateTimePickerPortalId);
|
|
1628
|
+
this.createDateTimePickerUsingPortal();
|
|
1629
|
+
}
|
|
1198
1630
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1199
1631
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideInputComponent, isStandalone: true, selector: "cide-ele-input", inputs: { fill: "fill", label: "label", labelHide: "labelHide", disabled: "disabled", clearInput: "clearInput", labelPlacement: "labelPlacement", labelDir: "labelDir", placeholder: "placeholder", leadingIcon: "leadingIcon", trailingIcon: "trailingIcon", helperText: "helperText", helperTextCollapse: "helperTextCollapse", hideHelperAndErrorText: "hideHelperAndErrorText", errorText: "errorText", maxlength: "maxlength", minlength: "minlength", required: "required", autocapitalize: "autocapitalize", autocomplete: "autocomplete", type: "type", width: "width", id: "id", ngModel: "ngModel", option: "option", min: "min", max: "max", size: "size" }, outputs: { ngModelChange: "ngModelChange" }, providers: [
|
|
1200
1632
|
{
|
|
@@ -1209,7 +1641,7 @@ class CideInputComponent {
|
|
|
1209
1641
|
useExisting: forwardRef(() => CideInputComponent),
|
|
1210
1642
|
},
|
|
1211
1643
|
CapitalizePipe
|
|
1212
|
-
], viewQueries: [{ propertyName: "datePickerTemplate", first: true, predicate: ["datePickerTemplate"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-input\">\n <!------------------------------------------TEXT | PASSWORD | DATE | URL------------------------------------------>\n @if (type === 'text' || type === 'number' || type === 'password' || type === 'email' || type === 'tel' || type === 'date' || type === 'url') {\n <div class=\"tw-w-full tw-relative\" [ngStyle]=\"{ width: width }\" [ngClass]=\"{\n 'cide-element-size-xxs': (size === '2xs'),\n 'cide-element-size-xs': (size === 'xs'),\n 'cide-element-size-sm': (size === 'sm'),\n 'cide-element-size-md': (size === 'md'),\n 'cide-element-size-lg': (size === 'lg'),\n 'cide-element-leading-icon': leadingIcon,\n 'cide-element-trailing-icon': trailingIconInternal,\n 'cide-element-clear-input': clearInput,\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\n 'cide-element-input-label-start': (labelDir === 'start'),\n 'cide-element-input-label-end': (labelDir === 'end'),\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\n 'cide-element-input-label-less': (!label || labelHide),\n 'cide-element-style-outline': (fill === 'outline'),\n 'cide-element-style-solid': (fill === 'solid'),\n 'cide-element-style-standard': (fill === 'standard'),\n 'cide-element-input-number': (type === 'number')\n }\">\n <!-- label -->\n @if (label && !labelHide) {\n <label [for]=\"id\" class=\"cide-input-label\">\n {{label}}\n @if (required) {\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\n }\n </label>\n }\n\n <!-- all one line elemets which dose not affect with label and error text -->\n <div class=\"cide-element-input-wrapper\">\n <!-- Leading Icon -->\n @if (leadingIcon) {\n <span class=\"cide-input-leading-icon-wrapper\">\n <span\n class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\n </span>\n }\n\n <!-- Trailing icon -->\n @if (trailingIconInternal) {\n <span class=\"tw-absolute cide-input-trailing-icon tw-select-none tw-right-0\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\n </span>\n }\n\n <!-- Clear -->\n @if (clearInput && ngModel) {\n <button class=\"cide-input-clear\" (click)=\"ClearInputValue()\">\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\n </button>\n }\n\n <!-- Date Input Wrapper -->\n @if (type === 'date') {\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\n <!-- Date Input (read-only to prevent manual input) -->\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n [value]=\"getDateDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\n [autocomplete]=\"autocomplete\"\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\n \n <!-- Placeholder overlay for empty date -->\n @if (!ngModel && placeholder) {\n <div class=\"cide-input-date-overlay\">\n {{placeholder}}\n </div>\n }\n\n <!-- Date picker is now rendered as a portal appended to body -->\n </div>\n }\n\n <!-- Regular Input (non-date, non-url) -->\n @if (type !== 'date' && type !== 'url') {\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\n [autocomplete]=\"autocomplete\" [min]=\"min\" [max]=\"max\"\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\n }\n\n <!-- URL Input -->\n @if (type === 'url') {\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\n [autocomplete]=\"autocomplete\"\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\n }\n </div>\n <!-- error text / helper text -->\n @if ((errorText || helperText || !helperTextCollapse) && !hideHelperAndErrorText) {\n <span class=\"cide-input-help-error-text\">{{\n isValid\n ? helperText : (errorText ?\n (isTouched ? errorText : helperText)\n : helperText)}}\n </span>\n }\n </div>\n }\n\n <!-- Input with tralling icon -->\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\n <div class=\"tw-w-fullh-full tw-relative\">\n <label\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\n <span class=\"tw-absolute -tw-bottom-px tw-right-0 -tw-z-10 tw-text-gray-400\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\n </span>\n <input\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-1 tw-pr-8 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\n value=\"Ankush Bhure\" />\n </div>\n </div> -->\n\n <!-- Input with leading icon -->\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\n <div class=\"tw-w-fullh-full tw-relative\">\n <label\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\n <span class=\"tw-absolute -tw-bottom-px tw-left-0 -tw-z-10 tw-text-gray-400\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\n </span>\n <input\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-8 tw-pr-1 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\n value=\"Ankush Bhure\" />\n </div>\n </div> -->\n\n <!------------------------------------------CHECKBOX------------------------------------------>\n @if (type === 'checkbox') {\n <div class=\"tw-flex\">\n <div class=\"cide-checkbox tw-relative\">\n <input [checked]=\"ngModel\" [value]=\"ngModel\" [id]=\"idRandom\" [type]=\"type\" [disabled]=\"disabled\"\n class=\"tw-absolute tw-left-0 tw-invisible\" (click)=\"updateValueCheckBox(!ngModel); focusControl()\"\n [autocomplete]=\"autocomplete\" />\n <label class=\"cide-checkbox-label tw-cursor-pointer\" [for]=\"idRandom\">\n <span class=\"tw-border-2 tw-border-solid tw-relative tw-rounded-md\">\n <svg width=\"12px\" height=\"10px\" class=\"tw-absolute\">\n <use xlink:href=\"#sdfwiorfklasfjjalfjwerwr\"></use>\n </svg>\n </span>\n @if (!labelHide) {\n <span class=\"tw-text-sm tw-pl-2 tw-leading-[18px] tw-select-none tw-cursor-pointer\">{{label}}</span>\n }\n </label>\n <svg class=\"tw-absolute tw-h-0 tw-w-0 tw-select-none tw-pointer-events-none\">\n <!-- Element hidden and its xpath is used to display inside SVG -->\n <symbol id=\"sdfwiorfklasfjjalfjwerwr\" viewbox=\"0 0 12 10\">\n <polyline points=\"1.5 6 4.5 9 10.5 1\"></polyline>\n </symbol>\n </svg>\n </div>\n </div>\n }\n\n <!-------------------------------------------SELECT------------------------------------------->\n @if (type === 'select') {\n <div>sas\n <div class=\"tw-relative\">\n <div class=\"tw-absolute\">\n @for (item of option; track $index) {\n <div class=\"tw-w-full\">\n {{item}}\n </div>\n }\n </div>\n </div>\n </div>\n }\n</div>\n\n<!-- Date Picker Template -->\n<ng-template #datePickerTemplate>\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4\">\n <!-- Date Picker Header -->\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-4\">\n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"previousMonth()\">\n <span class=\"material-symbols-outlined tw-text-base\">chevron_left</span>\n </button>\n \n <button \n type=\"button\" \n class=\"tw-text-sm tw-font-medium tw-text-gray-800 hover:tw-bg-gray-100 tw-px-3 tw-py-1 tw-rounded-md tw-transition-colors\"\n (click)=\"toggleMonthYearSelector()\">\n {{ monthNames[currentMonth] }} {{ currentYear }}\n </button>\n \n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"nextMonth()\">\n <span class=\"material-symbols-outlined tw-text-base\">chevron_right</span>\n </button>\n </div>\n\n <!-- Month/Year Selector -->\n @if (showMonthYearSelector) {\n <div class=\"tw-mb-4\">\n <!-- Year Navigation -->\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-3\">\n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"previousYear()\">\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_left</span>\n </button>\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-800\">{{ currentYear }}</span>\n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"nextYear()\">\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_right</span>\n </button>\n </div>\n \n <!-- Month Grid -->\n <div class=\"tw-grid tw-grid-cols-3 tw-gap-2 tw-mb-3\">\n @for (monthName of shortMonthNames; track $index) {\n <button \n type=\"button\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-rounded-md tw-transition-colors tw-text-center\"\n [class.tw-bg-blue-500]=\"$index === currentMonth\"\n [class.tw-text-white]=\"$index === currentMonth\"\n [class.tw-text-gray-700]=\"$index !== currentMonth\"\n [class.hover:tw-bg-blue-50]=\"$index !== currentMonth\"\n (click)=\"selectMonth($index)\">\n {{ monthName }}\n </button>\n }\n </div>\n \n <!-- Back to Calendar Button -->\n <div class=\"tw-text-center\">\n <button \n type=\"button\" \n class=\"tw-px-3 tw-py-1 tw-text-sm tw-text-blue-600 hover:tw-bg-blue-50 tw-rounded-md tw-transition-colors\"\n (click)=\"toggleMonthYearSelector()\">\n Back to Calendar\n </button>\n </div>\n </div>\n } @else {\n <!-- Calendar View -->\n <div>\n <!-- Days of Week Header -->\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1 tw-mb-2\">\n @for (dayName of ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; track dayName) {\n <div class=\"tw-text-center tw-text-xs tw-font-medium tw-text-gray-500 tw-py-2\">\n {{ dayName }}\n </div>\n }\n </div>\n\n <!-- Calendar Days -->\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1\">\n @for (dayInfo of calendarDays; track dayInfo.date.getTime()) {\n <button \n type=\"button\"\n class=\"tw-w-8 tw-h-8 tw-text-sm tw-rounded-md tw-transition-colors tw-flex tw-items-center tw-justify-center\"\n [class.tw-text-gray-400]=\"!dayInfo.isCurrentMonth\"\n [class.tw-text-gray-900]=\"dayInfo.isCurrentMonth\"\n [class.tw-bg-blue-500]=\"dayInfo.isSelected\"\n [class.tw-text-white]=\"dayInfo.isSelected\"\n [class.tw-bg-blue-100]=\"dayInfo.isToday && !dayInfo.isSelected\"\n [class.tw-text-blue-800]=\"dayInfo.isToday && !dayInfo.isSelected\"\n [class.hover:tw-bg-blue-50]=\"dayInfo.isCurrentMonth && !dayInfo.isSelected\"\n (click)=\"selectDate(dayInfo)\">\n {{ dayInfo.day }}\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Date Picker Footer -->\n <div class=\"tw-flex tw-justify-between tw-mt-4 tw-pt-3 tw-border-t tw-border-gray-200\">\n <button \n type=\"button\" \n class=\"tw-px-3 tw-py-1 tw-text-sm tw-text-gray-600 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors\"\n (click)=\"closeDatePicker()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"tw-px-3 tw-py-1 tw-text-sm tw-bg-blue-500 tw-text-white hover:tw-bg-blue-600 tw-rounded-md tw-transition-colors\"\n (click)=\"closeDatePicker()\">\n Done\n </button>\n </div>\n </div>\n</ng-template>", styles: ["input[type=date]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=date]::-webkit-inner-spin-button,input[type=date]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=date]{-moz-appearance:textfield}input[type=date]::-webkit-datetime-edit,input[type=date]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=date]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=date]{background:transparent!important;border:none!important;outline:none!important}.cide-input-date-wrapper{position:relative;width:100%}.cide-input-date-overlay{position:absolute;inset:0;pointer-events:none;display:flex;align-items:center;padding-left:.25rem;color:#6b7280}.cide-input-date-has-value .cide-input-date-overlay{display:none}.cide-date-picker-panel{min-width:320px;max-width:400px;z-index:1000}.cide-date-picker-panel{animation:datePickerFadeIn .2s ease-out}@keyframes datePickerFadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.cide-date-picker-panel button:disabled{opacity:.5;cursor:not-allowed}.cide-date-picker-panel button:focus{outline:2px solid rgb(59,130,246);outline-offset:2px}.cide-date-picker-panel button:hover:not(:disabled){transform:scale(1.05);transition:transform .1s ease-in-out}.cide-date-picker-panel button.today:not(.selected){box-shadow:0 0 0 1px #3b82f6}.cide-date-picker-panel button.selected{box-shadow:0 2px 4px #3b82f64d;font-weight:600}.cide-date-picker-panel .nav-button{transition:all .2s ease-in-out}.cide-date-picker-panel .nav-button:hover{background-color:#f3f4f6;transform:scale(1.1)}.cide-date-picker-panel h3{-webkit-user-select:none;user-select:none;transition:color .2s ease-in-out}.cide-date-picker-panel .days-header{border-bottom:1px solid rgb(229,231,235);margin-bottom:.5rem;padding-bottom:.5rem}.cide-date-picker-portal{box-shadow:0 10px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:.5rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out}.cide-date-picker-portal{animation:portalFadeIn .2s ease-out}@keyframes portalFadeIn{0%{opacity:0;transform:translateY(-8px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.cide-date-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}\n"], dependencies: [{ kind: "ngmodule", type:
|
|
1644
|
+
], viewQueries: [{ propertyName: "datePickerTemplate", first: true, predicate: ["datePickerTemplate"], descendants: true, static: true }, { propertyName: "timePickerTemplate", first: true, predicate: ["timePickerTemplate"], descendants: true, static: true }, { propertyName: "dateTimePickerTemplate", first: true, predicate: ["dateTimePickerTemplate"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-input\">\r\n <!------------------------------------------TEXT | PASSWORD | DATE | URL------------------------------------------>\r\n @if (type === 'text' || type === 'number' || type === 'password' || type === 'email' || type === 'tel' || type === 'date' || type === 'datetime-local' || type === 'url') {\r\n <div class=\"tw-w-full tw-relative\" [ngStyle]=\"{ width: width }\" [ngClass]=\"{\r\n 'cide-element-size-xxs': (size === '2xs'),\r\n 'cide-element-size-xs': (size === 'xs'),\r\n 'cide-element-size-sm': (size === 'sm'),\r\n 'cide-element-size-md': (size === 'md'),\r\n 'cide-element-size-lg': (size === 'lg'),\r\n 'cide-element-leading-icon': leadingIcon,\r\n 'cide-element-trailing-icon': trailingIconInternal,\r\n 'cide-element-clear-input': clearInput,\r\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\r\n 'cide-element-input-label-start': (labelDir === 'start'),\r\n 'cide-element-input-label-end': (labelDir === 'end'),\r\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\r\n 'cide-element-input-label-less': (!label || labelHide),\r\n 'cide-element-style-outline': (fill === 'outline'),\r\n 'cide-element-style-solid': (fill === 'solid'),\r\n 'cide-element-style-standard': (fill === 'standard'),\r\n 'cide-element-input-number': (type === 'number')\r\n }\">\r\n <!-- label -->\r\n @if (label && !labelHide) {\r\n <label [for]=\"id\" class=\"cide-input-label\">\r\n {{label}}\r\n @if (required) {\r\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <!-- all one line elemets which dose not affect with label and error text -->\r\n <div class=\"cide-element-input-wrapper\">\r\n <!-- Leading Icon -->\r\n @if (leadingIcon) {\r\n <span class=\"cide-input-leading-icon-wrapper\">\r\n <span\r\n class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\r\n </span>\r\n }\r\n\r\n <!-- Trailing icon -->\r\n @if (trailingIconInternal) {\r\n <span class=\"tw-absolute cide-input-trailing-icon tw-select-none tw-right-0\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\r\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\r\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\r\n </span>\r\n }\r\n\r\n <!-- Clear -->\r\n @if (clearInput && ngModel) {\r\n <button class=\"cide-input-clear\" (click)=\"ClearInputValue()\">\r\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\r\n </button>\r\n }\r\n\r\n <!-- Date Input Wrapper -->\r\n @if (type === 'date') {\r\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\r\n <!-- Date Input (read-only to prevent manual input) -->\r\n <input [placeholder]=\"placeholder\" [id]=\"id || idRandom\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [value]=\"getDateDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\r\n \r\n <!-- Placeholder overlay for empty date -->\r\n @if (!ngModel && placeholder) {\r\n <div class=\"cide-input-date-overlay\">\r\n {{placeholder}}\r\n </div>\r\n }\r\n\r\n <!-- Date picker is now rendered as a portal appended to body -->\r\n </div>\r\n }\r\n\r\n <!-- Time Input Wrapper -->\r\n @if (isTimeType()) {\r\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\r\n <!-- Time Input (read-only to prevent manual input) -->\r\n <input [placeholder]=\"placeholder\" [id]=\"id || idRandom\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [value]=\"getTimeDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\r\n \r\n <!-- Placeholder overlay for empty time -->\r\n @if (!ngModel && placeholder) {\r\n <div class=\"cide-input-date-overlay\">\r\n {{placeholder}}\r\n </div>\r\n }\r\n\r\n <!-- Time picker is now rendered as a portal appended to body -->\r\n </div>\r\n }\r\n\r\n <!-- DateTime Local Input -->\r\n @if (type === 'datetime-local') {\r\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\r\n <!-- DateTime Input (read-only to prevent manual input) -->\r\n <input [placeholder]=\"placeholder\" [id]=\"id || idRandom\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [value]=\"getDateTimeDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\r\n \r\n <!-- Placeholder overlay for empty datetime -->\r\n @if (!ngModel && placeholder) {\r\n <div class=\"cide-input-date-overlay\">\r\n {{placeholder}}\r\n </div>\r\n }\r\n\r\n <!-- DateTime picker is now rendered as a portal appended to body -->\r\n </div>\r\n }\r\n\r\n <!-- Regular Input (non-date, non-time, non-datetime-local, non-url) -->\r\n @if (type !== 'date' && !isTimeType() && type !== 'datetime-local' && type !== 'url') {\r\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\r\n [autocomplete]=\"autocomplete\" [min]=\"min\" [max]=\"max\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\r\n }\r\n\r\n <!-- URL Input -->\r\n @if (type === 'url') {\r\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\r\n }\r\n </div>\r\n <!-- error text / helper text -->\r\n @if ((errorText || helperText || !helperTextCollapse) && !hideHelperAndErrorText) {\r\n <span class=\"cide-input-help-error-text\">{{\r\n isValid\r\n ? helperText : (errorText ?\r\n (isTouched ? errorText : helperText)\r\n : helperText)}}\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Input with tralling icon -->\r\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\r\n <div class=\"tw-w-fullh-full tw-relative\">\r\n <label\r\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\r\n <span class=\"tw-absolute -tw-bottom-px tw-right-0 -tw-z-10 tw-text-gray-400\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\r\n </span>\r\n <input\r\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-1 tw-pr-8 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\r\n value=\"Ankush Bhure\" />\r\n </div>\r\n </div> -->\r\n\r\n <!-- Input with leading icon -->\r\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\r\n <div class=\"tw-w-fullh-full tw-relative\">\r\n <label\r\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\r\n <span class=\"tw-absolute -tw-bottom-px tw-left-0 -tw-z-10 tw-text-gray-400\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\r\n </span>\r\n <input\r\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-8 tw-pr-1 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\r\n value=\"Ankush Bhure\" />\r\n </div>\r\n </div> -->\r\n\r\n <!------------------------------------------CHECKBOX------------------------------------------>\r\n @if (type === 'checkbox') {\r\n <div class=\"tw-flex\">\r\n <div class=\"cide-checkbox tw-relative\">\r\n <input [checked]=\"ngModel\" [value]=\"ngModel\" [id]=\"idRandom\" [type]=\"type\" [disabled]=\"disabled\"\r\n class=\"tw-absolute tw-left-0 tw-invisible\" (click)=\"updateValueCheckBox(!ngModel); focusControl()\"\r\n [autocomplete]=\"autocomplete\" />\r\n <label class=\"cide-checkbox-label tw-cursor-pointer\" [for]=\"idRandom\">\r\n <span class=\"tw-border-2 tw-border-solid tw-relative tw-rounded-md\">\r\n <svg width=\"12px\" height=\"10px\" class=\"tw-absolute\">\r\n <use xlink:href=\"#sdfwiorfklasfjjalfjwerwr\"></use>\r\n </svg>\r\n </span>\r\n @if (!labelHide) {\r\n <span class=\"tw-text-sm tw-pl-2 tw-leading-[18px] tw-select-none tw-cursor-pointer\">{{label}}</span>\r\n }\r\n </label>\r\n <svg class=\"tw-absolute tw-h-0 tw-w-0 tw-select-none tw-pointer-events-none\">\r\n <!-- Element hidden and its xpath is used to display inside SVG -->\r\n <symbol id=\"sdfwiorfklasfjjalfjwerwr\" viewbox=\"0 0 12 10\">\r\n <polyline points=\"1.5 6 4.5 9 10.5 1\"></polyline>\r\n </symbol>\r\n </svg>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-------------------------------------------SELECT------------------------------------------->\r\n @if (type === 'select') {\r\n <div>sas\r\n <div class=\"tw-relative\">\r\n <div class=\"tw-absolute\">\r\n @for (item of option; track $index) {\r\n <div class=\"tw-w-full\">\r\n {{item}}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Date Picker Template -->\r\n<ng-template #datePickerTemplate>\r\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4\">\r\n <!-- Date Picker Header -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-4\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_left</span>\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-text-sm tw-font-medium tw-text-gray-800 hover:tw-bg-gray-100 tw-px-3 tw-py-1 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n {{ monthNames[currentMonth] }} {{ currentYear }}\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_right</span>\r\n </button>\r\n </div>\r\n\r\n <!-- Month/Year Selector -->\r\n @if (showMonthYearSelector) {\r\n <div class=\"tw-mb-4\">\r\n <!-- Year Navigation -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-3\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_left</span>\r\n </button>\r\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-800\">{{ currentYear }}</span>\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_right</span>\r\n </button>\r\n </div>\r\n \r\n <!-- Month Grid -->\r\n <div class=\"tw-grid tw-grid-cols-3 tw-gap-2 tw-mb-3\">\r\n @for (monthName of shortMonthNames; track $index) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-rounded-md tw-transition-colors tw-text-center\"\r\n [class.tw-bg-blue-500]=\"$index === currentMonth\"\r\n [class.tw-text-white]=\"$index === currentMonth\"\r\n [class.tw-text-gray-700]=\"$index !== currentMonth\"\r\n [class.hover:tw-bg-blue-50]=\"$index !== currentMonth\"\r\n (click)=\"selectMonth($index)\">\r\n {{ monthName }}\r\n </button>\r\n }\r\n </div>\r\n \r\n <!-- Back to Calendar Button -->\r\n <div class=\"tw-text-center\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-sm tw-text-blue-600 hover:tw-bg-blue-50 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n Back to Calendar\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Calendar View -->\r\n <div>\r\n <!-- Days of Week Header -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1 tw-mb-2\">\r\n @for (dayName of ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; track dayName) {\r\n <div class=\"tw-text-center tw-text-xs tw-font-medium tw-text-gray-500 tw-py-2\">\r\n {{ dayName }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Calendar Days -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1\">\r\n @for (dayInfo of calendarDays; track dayInfo.date.getTime()) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-text-sm tw-rounded-md tw-transition-colors tw-flex tw-items-center tw-justify-center\"\r\n [class.tw-text-gray-400]=\"!dayInfo.isCurrentMonth\"\r\n [class.tw-text-gray-900]=\"dayInfo.isCurrentMonth\"\r\n [class.tw-bg-blue-500]=\"dayInfo.isSelected\"\r\n [class.tw-text-white]=\"dayInfo.isSelected\"\r\n [class.tw-bg-blue-100]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.tw-text-blue-800]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.hover:tw-bg-blue-50]=\"dayInfo.isCurrentMonth && !dayInfo.isSelected\"\r\n (click)=\"showDateTimePicker ? selectDateTimeDate(dayInfo) : selectDate(dayInfo)\">\r\n {{ dayInfo.day }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Date Picker Footer -->\r\n <div class=\"tw-flex tw-justify-between tw-mt-3 tw-pt-2 tw-border-t tw-border-gray-200\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-text-gray-600 hover:tw-bg-gray-100 tw-rounded tw-transition-colors\"\r\n (click)=\"closeDatePicker()\">\r\n Cancel\r\n </button>\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700 tw-rounded tw-transition-colors\"\r\n (click)=\"closeDatePicker()\">\r\n Done\r\n </button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<!-- Time Picker Template -->\r\n<ng-template #timePickerTemplate>\r\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4 tw-w-72\">\r\n <!-- Time Picker Header -->\r\n <div class=\"tw-text-center tw-mb-3\">\r\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-800 tw-m-0\">Time</h3>\r\n </div>\r\n\r\n <!-- Time Spinners Container -->\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-gap-3 tw-mb-4\">\r\n <!-- Hours -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-12 tw-text-center\">\r\n {{ timeHours }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">hour</span>\r\n </div>\r\n\r\n <!-- Colon Separator -->\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-mb-4\">:</div>\r\n\r\n <!-- Minutes -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-12 tw-text-center\">\r\n {{ timeMinutes.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">min</span>\r\n </div>\r\n\r\n <!-- Colon Separator -->\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-mb-4\">:</div>\r\n\r\n <!-- Seconds -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-12 tw-text-center\">\r\n {{ timeSeconds.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">sec</span>\r\n </div>\r\n </div>\r\n\r\n <!-- AM/PM Toggle -->\r\n <div class=\"tw-flex tw-justify-center tw-mb-3\">\r\n <div class=\"tw-inline-flex tw-rounded-md tw-bg-gray-100 tw-p-0.5\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-4 tw-py-1.5 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'AM'\"\r\n [class.tw-shadow]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'AM'\"\r\n (click)=\"timeFormat = 'AM'\">\r\n AM\r\n </button>\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-4 tw-py-1.5 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'PM'\"\r\n [class.tw-shadow]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'PM'\"\r\n (click)=\"timeFormat = 'PM'\">\r\n PM\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Current Time Display -->\r\n <div class=\"tw-text-center tw-mb-3\">\r\n <span class=\"tw-text-sm tw-text-gray-600\">\r\n {{ timeHours }}:{{ timeMinutes.toString().padStart(2, '0') }}:{{ timeSeconds.toString().padStart(2, '0') }} {{ timeFormat }}\r\n </span>\r\n </div>\r\n\r\n <!-- Time Picker Footer -->\r\n <div class=\"tw-flex tw-gap-2\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-text-gray-700 tw-bg-white tw-border tw-border-gray-300 hover:tw-bg-gray-50 tw-rounded tw-transition-colors\"\r\n (click)=\"showDateTimePicker ? closeDateTimePicker() : closeTimePicker()\">\r\n Cancel\r\n </button>\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700 tw-rounded tw-transition-colors tw-border-0\"\r\n (click)=\"showDateTimePicker ? applyDateTime() : applyTime()\">\r\n OK\r\n </button>\r\n </div>\r\n\r\n </div>\r\n</ng-template>\r\n\r\n<!-- DateTime Picker Template (Combined Date + Time) -->\r\n<ng-template #dateTimePickerTemplate>\r\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4\">\r\n <!-- DateTime Picker Header -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-4\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_left</span>\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-text-sm tw-font-medium tw-text-gray-800 hover:tw-bg-gray-100 tw-px-3 tw-py-1 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n {{ monthNames[currentMonth] }} {{ currentYear }}\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_right</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"tw-grid tw-grid-cols-2 tw-gap-4\">\r\n <!-- Left Side: Calendar -->\r\n <div>\r\n <!-- Month/Year Selector -->\r\n @if (showMonthYearSelector) {\r\n <div class=\"tw-mb-4\">\r\n <!-- Year Navigation -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-3\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_left</span>\r\n </button>\r\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-800\">{{ currentYear }}</span>\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_right</span>\r\n </button>\r\n </div>\r\n \r\n <!-- Month Grid -->\r\n <div class=\"tw-grid tw-grid-cols-3 tw-gap-2 tw-mb-3\">\r\n @for (monthName of shortMonthNames; track $index) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-2 tw-py-1.5 tw-text-xs tw-rounded-md tw-transition-colors tw-text-center\"\r\n [class.tw-bg-blue-500]=\"$index === currentMonth\"\r\n [class.tw-text-white]=\"$index === currentMonth\"\r\n [class.tw-text-gray-700]=\"$index !== currentMonth\"\r\n [class.hover:tw-bg-blue-50]=\"$index !== currentMonth\"\r\n (click)=\"selectMonth($index)\">\r\n {{ monthName }}\r\n </button>\r\n }\r\n </div>\r\n \r\n <!-- Back to Calendar Button -->\r\n <div class=\"tw-text-center\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-text-blue-600 hover:tw-bg-blue-50 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n Back to Calendar\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Calendar View -->\r\n <div>\r\n <!-- Days of Week Header -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1 tw-mb-2\">\r\n @for (dayName of ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; track dayName) {\r\n <div class=\"tw-text-center tw-text-xs tw-font-medium tw-text-gray-500 tw-py-1\">\r\n {{ dayName }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Calendar Days -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1\">\r\n @for (dayInfo of calendarDays; track dayInfo.date.getTime()) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-text-xs tw-rounded-md tw-transition-colors tw-flex tw-items-center tw-justify-center\"\r\n [class.tw-text-gray-400]=\"!dayInfo.isCurrentMonth\"\r\n [class.tw-text-gray-900]=\"dayInfo.isCurrentMonth\"\r\n [class.tw-bg-blue-500]=\"dayInfo.isSelected\"\r\n [class.tw-text-white]=\"dayInfo.isSelected\"\r\n [class.tw-bg-blue-100]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.tw-text-blue-800]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.hover:tw-bg-blue-50]=\"dayInfo.isCurrentMonth && !dayInfo.isSelected\"\r\n (click)=\"selectDateTimeDate(dayInfo)\">\r\n {{ dayInfo.day }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Right Side: Time Picker -->\r\n <div class=\"tw-border-l tw-border-gray-200 tw-pl-4\">\r\n <div class=\"tw-text-center tw-mb-2\">\r\n <h4 class=\"tw-text-sm tw-font-semibold tw-text-gray-800 tw-m-0\">Time</h4>\r\n </div>\r\n\r\n <!-- Time Spinners -->\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-gap-2 tw-mb-3\">\r\n <!-- Hours -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-10 tw-text-center\">\r\n {{ timeHours }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">hour</span>\r\n </div>\r\n\r\n <!-- Colon -->\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-mb-3\">:</div>\r\n\r\n <!-- Minutes -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-10 tw-text-center\">\r\n {{ timeMinutes.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">min</span>\r\n </div>\r\n\r\n <!-- Colon -->\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-mb-3\">:</div>\r\n\r\n <!-- Seconds -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-10 tw-text-center\">\r\n {{ timeSeconds.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">sec</span>\r\n </div>\r\n </div>\r\n\r\n <!-- AM/PM Toggle -->\r\n <div class=\"tw-flex tw-justify-center tw-mb-2\">\r\n <div class=\"tw-inline-flex tw-rounded-md tw-bg-gray-100 tw-p-0.5\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'AM'\"\r\n [class.tw-shadow-sm]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'AM'\"\r\n (click)=\"timeFormat = 'AM'\">\r\n AM\r\n </button>\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'PM'\"\r\n [class.tw-shadow-sm]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'PM'\"\r\n (click)=\"timeFormat = 'PM'\">\r\n PM\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Current Time Display -->\r\n <div class=\"tw-text-center tw-text-xs tw-text-gray-600\">\r\n {{ timeHours }}:{{ timeMinutes.toString().padStart(2, '0') }}:{{ timeSeconds.toString().padStart(2, '0') }} {{ timeFormat }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DateTime Picker Footer -->\r\n <div class=\"tw-flex tw-gap-2 tw-mt-3 tw-pt-2 tw-border-t tw-border-gray-200\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-text-gray-700 tw-bg-white tw-border tw-border-gray-300 hover:tw-bg-gray-50 tw-rounded tw-transition-colors\"\r\n (click)=\"closeDateTimePicker()\">\r\n Cancel\r\n </button>\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700 tw-rounded tw-transition-colors tw-border-0\"\r\n (click)=\"applyDateTime()\">\r\n OK\r\n </button>\r\n </div>\r\n </div>\r\n</ng-template>", styles: ["input[type=date]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=date]::-webkit-inner-spin-button,input[type=date]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=date]{-moz-appearance:textfield}input[type=date]::-webkit-datetime-edit,input[type=date]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=date]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=date]{background:transparent!important;border:none!important;outline:none!important}.cide-input-date-wrapper{position:relative;width:100%}.cide-input-date-overlay{position:absolute;inset:0;pointer-events:none;display:flex;align-items:center;padding-left:.25rem;color:#6b7280}.cide-input-date-has-value .cide-input-date-overlay{display:none}.cide-date-picker-panel{min-width:320px;max-width:400px;z-index:1000}.cide-date-picker-panel{animation:datePickerFadeIn .2s ease-out}@keyframes datePickerFadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.cide-date-picker-panel button:disabled{opacity:.5;cursor:not-allowed}.cide-date-picker-panel button:focus{outline:2px solid rgb(59,130,246);outline-offset:2px}.cide-date-picker-panel button:hover:not(:disabled){transform:scale(1.05);transition:transform .1s ease-in-out}.cide-date-picker-panel button.today:not(.selected){box-shadow:0 0 0 1px #3b82f6}.cide-date-picker-panel button.selected{box-shadow:0 2px 4px #3b82f64d;font-weight:600}.cide-date-picker-panel .nav-button{transition:all .2s ease-in-out}.cide-date-picker-panel .nav-button:hover{background-color:#f3f4f6;transform:scale(1.1)}.cide-date-picker-panel h3{-webkit-user-select:none;user-select:none;transition:color .2s ease-in-out}.cide-date-picker-panel .days-header{border-bottom:1px solid rgb(229,231,235);margin-bottom:.5rem;padding-bottom:.5rem}.cide-date-picker-portal{box-shadow:0 10px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:.5rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out}.cide-date-picker-portal{animation:portalFadeIn .2s ease-out}@keyframes portalFadeIn{0%{opacity:0;transform:translateY(-8px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.cide-date-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}input[type=time]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=time]::-webkit-inner-spin-button,input[type=time]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=time]{-moz-appearance:textfield}input[type=time]::-webkit-datetime-edit,input[type=time]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=time]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=time]{background:transparent!important;border:none!important;outline:none!important}.cide-time-picker-portal{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:1rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out;animation:portalFadeIn .2s ease-out}.cide-time-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}input[type=datetime-local]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=datetime-local]::-webkit-inner-spin-button,input[type=datetime-local]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=datetime-local]{-moz-appearance:textfield}input[type=datetime-local]::-webkit-datetime-edit,input[type=datetime-local]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=datetime-local]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=datetime-local]{background:transparent!important;border:none!important;outline:none!important}.cide-datetime-picker-portal{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:1rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out;animation:portalFadeIn .2s ease-out}.cide-datetime-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}\n"], dependencies: [{ kind: "ngmodule", type:
|
|
1213
1645
|
// directives
|
|
1214
1646
|
CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type:
|
|
1215
1647
|
// for ngModel
|
|
@@ -1235,7 +1667,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
1235
1667
|
useExisting: forwardRef(() => CideInputComponent),
|
|
1236
1668
|
},
|
|
1237
1669
|
CapitalizePipe
|
|
1238
|
-
], template: "<div class=\"cide-input\">\n <!------------------------------------------TEXT | PASSWORD | DATE | URL------------------------------------------>\n @if (type === 'text' || type === 'number' || type === 'password' || type === 'email' || type === 'tel' || type === 'date' || type === 'url') {\n <div class=\"tw-w-full tw-relative\" [ngStyle]=\"{ width: width }\" [ngClass]=\"{\n 'cide-element-size-xxs': (size === '2xs'),\n 'cide-element-size-xs': (size === 'xs'),\n 'cide-element-size-sm': (size === 'sm'),\n 'cide-element-size-md': (size === 'md'),\n 'cide-element-size-lg': (size === 'lg'),\n 'cide-element-leading-icon': leadingIcon,\n 'cide-element-trailing-icon': trailingIconInternal,\n 'cide-element-clear-input': clearInput,\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\n 'cide-element-input-label-start': (labelDir === 'start'),\n 'cide-element-input-label-end': (labelDir === 'end'),\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\n 'cide-element-input-label-less': (!label || labelHide),\n 'cide-element-style-outline': (fill === 'outline'),\n 'cide-element-style-solid': (fill === 'solid'),\n 'cide-element-style-standard': (fill === 'standard'),\n 'cide-element-input-number': (type === 'number')\n }\">\n <!-- label -->\n @if (label && !labelHide) {\n <label [for]=\"id\" class=\"cide-input-label\">\n {{label}}\n @if (required) {\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\n }\n </label>\n }\n\n <!-- all one line elemets which dose not affect with label and error text -->\n <div class=\"cide-element-input-wrapper\">\n <!-- Leading Icon -->\n @if (leadingIcon) {\n <span class=\"cide-input-leading-icon-wrapper\">\n <span\n class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\n </span>\n }\n\n <!-- Trailing icon -->\n @if (trailingIconInternal) {\n <span class=\"tw-absolute cide-input-trailing-icon tw-select-none tw-right-0\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\n </span>\n }\n\n <!-- Clear -->\n @if (clearInput && ngModel) {\n <button class=\"cide-input-clear\" (click)=\"ClearInputValue()\">\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\n </button>\n }\n\n <!-- Date Input Wrapper -->\n @if (type === 'date') {\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\n <!-- Date Input (read-only to prevent manual input) -->\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n [value]=\"getDateDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\n [autocomplete]=\"autocomplete\"\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\n \n <!-- Placeholder overlay for empty date -->\n @if (!ngModel && placeholder) {\n <div class=\"cide-input-date-overlay\">\n {{placeholder}}\n </div>\n }\n\n <!-- Date picker is now rendered as a portal appended to body -->\n </div>\n }\n\n <!-- Regular Input (non-date, non-url) -->\n @if (type !== 'date' && type !== 'url') {\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\n [autocomplete]=\"autocomplete\" [min]=\"min\" [max]=\"max\"\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\n }\n\n <!-- URL Input -->\n @if (type === 'url') {\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\n [autocomplete]=\"autocomplete\"\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\n }\n </div>\n <!-- error text / helper text -->\n @if ((errorText || helperText || !helperTextCollapse) && !hideHelperAndErrorText) {\n <span class=\"cide-input-help-error-text\">{{\n isValid\n ? helperText : (errorText ?\n (isTouched ? errorText : helperText)\n : helperText)}}\n </span>\n }\n </div>\n }\n\n <!-- Input with tralling icon -->\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\n <div class=\"tw-w-fullh-full tw-relative\">\n <label\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\n <span class=\"tw-absolute -tw-bottom-px tw-right-0 -tw-z-10 tw-text-gray-400\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\n </span>\n <input\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-1 tw-pr-8 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\n value=\"Ankush Bhure\" />\n </div>\n </div> -->\n\n <!-- Input with leading icon -->\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\n <div class=\"tw-w-fullh-full tw-relative\">\n <label\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\n <span class=\"tw-absolute -tw-bottom-px tw-left-0 -tw-z-10 tw-text-gray-400\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\n </span>\n <input\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-8 tw-pr-1 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\n value=\"Ankush Bhure\" />\n </div>\n </div> -->\n\n <!------------------------------------------CHECKBOX------------------------------------------>\n @if (type === 'checkbox') {\n <div class=\"tw-flex\">\n <div class=\"cide-checkbox tw-relative\">\n <input [checked]=\"ngModel\" [value]=\"ngModel\" [id]=\"idRandom\" [type]=\"type\" [disabled]=\"disabled\"\n class=\"tw-absolute tw-left-0 tw-invisible\" (click)=\"updateValueCheckBox(!ngModel); focusControl()\"\n [autocomplete]=\"autocomplete\" />\n <label class=\"cide-checkbox-label tw-cursor-pointer\" [for]=\"idRandom\">\n <span class=\"tw-border-2 tw-border-solid tw-relative tw-rounded-md\">\n <svg width=\"12px\" height=\"10px\" class=\"tw-absolute\">\n <use xlink:href=\"#sdfwiorfklasfjjalfjwerwr\"></use>\n </svg>\n </span>\n @if (!labelHide) {\n <span class=\"tw-text-sm tw-pl-2 tw-leading-[18px] tw-select-none tw-cursor-pointer\">{{label}}</span>\n }\n </label>\n <svg class=\"tw-absolute tw-h-0 tw-w-0 tw-select-none tw-pointer-events-none\">\n <!-- Element hidden and its xpath is used to display inside SVG -->\n <symbol id=\"sdfwiorfklasfjjalfjwerwr\" viewbox=\"0 0 12 10\">\n <polyline points=\"1.5 6 4.5 9 10.5 1\"></polyline>\n </symbol>\n </svg>\n </div>\n </div>\n }\n\n <!-------------------------------------------SELECT------------------------------------------->\n @if (type === 'select') {\n <div>sas\n <div class=\"tw-relative\">\n <div class=\"tw-absolute\">\n @for (item of option; track $index) {\n <div class=\"tw-w-full\">\n {{item}}\n </div>\n }\n </div>\n </div>\n </div>\n }\n</div>\n\n<!-- Date Picker Template -->\n<ng-template #datePickerTemplate>\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4\">\n <!-- Date Picker Header -->\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-4\">\n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"previousMonth()\">\n <span class=\"material-symbols-outlined tw-text-base\">chevron_left</span>\n </button>\n \n <button \n type=\"button\" \n class=\"tw-text-sm tw-font-medium tw-text-gray-800 hover:tw-bg-gray-100 tw-px-3 tw-py-1 tw-rounded-md tw-transition-colors\"\n (click)=\"toggleMonthYearSelector()\">\n {{ monthNames[currentMonth] }} {{ currentYear }}\n </button>\n \n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"nextMonth()\">\n <span class=\"material-symbols-outlined tw-text-base\">chevron_right</span>\n </button>\n </div>\n\n <!-- Month/Year Selector -->\n @if (showMonthYearSelector) {\n <div class=\"tw-mb-4\">\n <!-- Year Navigation -->\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-3\">\n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"previousYear()\">\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_left</span>\n </button>\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-800\">{{ currentYear }}</span>\n <button \n type=\"button\" \n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\n (click)=\"nextYear()\">\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_right</span>\n </button>\n </div>\n \n <!-- Month Grid -->\n <div class=\"tw-grid tw-grid-cols-3 tw-gap-2 tw-mb-3\">\n @for (monthName of shortMonthNames; track $index) {\n <button \n type=\"button\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-rounded-md tw-transition-colors tw-text-center\"\n [class.tw-bg-blue-500]=\"$index === currentMonth\"\n [class.tw-text-white]=\"$index === currentMonth\"\n [class.tw-text-gray-700]=\"$index !== currentMonth\"\n [class.hover:tw-bg-blue-50]=\"$index !== currentMonth\"\n (click)=\"selectMonth($index)\">\n {{ monthName }}\n </button>\n }\n </div>\n \n <!-- Back to Calendar Button -->\n <div class=\"tw-text-center\">\n <button \n type=\"button\" \n class=\"tw-px-3 tw-py-1 tw-text-sm tw-text-blue-600 hover:tw-bg-blue-50 tw-rounded-md tw-transition-colors\"\n (click)=\"toggleMonthYearSelector()\">\n Back to Calendar\n </button>\n </div>\n </div>\n } @else {\n <!-- Calendar View -->\n <div>\n <!-- Days of Week Header -->\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1 tw-mb-2\">\n @for (dayName of ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; track dayName) {\n <div class=\"tw-text-center tw-text-xs tw-font-medium tw-text-gray-500 tw-py-2\">\n {{ dayName }}\n </div>\n }\n </div>\n\n <!-- Calendar Days -->\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1\">\n @for (dayInfo of calendarDays; track dayInfo.date.getTime()) {\n <button \n type=\"button\"\n class=\"tw-w-8 tw-h-8 tw-text-sm tw-rounded-md tw-transition-colors tw-flex tw-items-center tw-justify-center\"\n [class.tw-text-gray-400]=\"!dayInfo.isCurrentMonth\"\n [class.tw-text-gray-900]=\"dayInfo.isCurrentMonth\"\n [class.tw-bg-blue-500]=\"dayInfo.isSelected\"\n [class.tw-text-white]=\"dayInfo.isSelected\"\n [class.tw-bg-blue-100]=\"dayInfo.isToday && !dayInfo.isSelected\"\n [class.tw-text-blue-800]=\"dayInfo.isToday && !dayInfo.isSelected\"\n [class.hover:tw-bg-blue-50]=\"dayInfo.isCurrentMonth && !dayInfo.isSelected\"\n (click)=\"selectDate(dayInfo)\">\n {{ dayInfo.day }}\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Date Picker Footer -->\n <div class=\"tw-flex tw-justify-between tw-mt-4 tw-pt-3 tw-border-t tw-border-gray-200\">\n <button \n type=\"button\" \n class=\"tw-px-3 tw-py-1 tw-text-sm tw-text-gray-600 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors\"\n (click)=\"closeDatePicker()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"tw-px-3 tw-py-1 tw-text-sm tw-bg-blue-500 tw-text-white hover:tw-bg-blue-600 tw-rounded-md tw-transition-colors\"\n (click)=\"closeDatePicker()\">\n Done\n </button>\n </div>\n </div>\n</ng-template>", styles: ["input[type=date]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=date]::-webkit-inner-spin-button,input[type=date]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=date]{-moz-appearance:textfield}input[type=date]::-webkit-datetime-edit,input[type=date]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=date]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=date]{background:transparent!important;border:none!important;outline:none!important}.cide-input-date-wrapper{position:relative;width:100%}.cide-input-date-overlay{position:absolute;inset:0;pointer-events:none;display:flex;align-items:center;padding-left:.25rem;color:#6b7280}.cide-input-date-has-value .cide-input-date-overlay{display:none}.cide-date-picker-panel{min-width:320px;max-width:400px;z-index:1000}.cide-date-picker-panel{animation:datePickerFadeIn .2s ease-out}@keyframes datePickerFadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.cide-date-picker-panel button:disabled{opacity:.5;cursor:not-allowed}.cide-date-picker-panel button:focus{outline:2px solid rgb(59,130,246);outline-offset:2px}.cide-date-picker-panel button:hover:not(:disabled){transform:scale(1.05);transition:transform .1s ease-in-out}.cide-date-picker-panel button.today:not(.selected){box-shadow:0 0 0 1px #3b82f6}.cide-date-picker-panel button.selected{box-shadow:0 2px 4px #3b82f64d;font-weight:600}.cide-date-picker-panel .nav-button{transition:all .2s ease-in-out}.cide-date-picker-panel .nav-button:hover{background-color:#f3f4f6;transform:scale(1.1)}.cide-date-picker-panel h3{-webkit-user-select:none;user-select:none;transition:color .2s ease-in-out}.cide-date-picker-panel .days-header{border-bottom:1px solid rgb(229,231,235);margin-bottom:.5rem;padding-bottom:.5rem}.cide-date-picker-portal{box-shadow:0 10px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:.5rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out}.cide-date-picker-portal{animation:portalFadeIn .2s ease-out}@keyframes portalFadeIn{0%{opacity:0;transform:translateY(-8px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.cide-date-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}\n"] }]
|
|
1670
|
+
], template: "<div class=\"cide-input\">\r\n <!------------------------------------------TEXT | PASSWORD | DATE | URL------------------------------------------>\r\n @if (type === 'text' || type === 'number' || type === 'password' || type === 'email' || type === 'tel' || type === 'date' || type === 'datetime-local' || type === 'url') {\r\n <div class=\"tw-w-full tw-relative\" [ngStyle]=\"{ width: width }\" [ngClass]=\"{\r\n 'cide-element-size-xxs': (size === '2xs'),\r\n 'cide-element-size-xs': (size === 'xs'),\r\n 'cide-element-size-sm': (size === 'sm'),\r\n 'cide-element-size-md': (size === 'md'),\r\n 'cide-element-size-lg': (size === 'lg'),\r\n 'cide-element-leading-icon': leadingIcon,\r\n 'cide-element-trailing-icon': trailingIconInternal,\r\n 'cide-element-clear-input': clearInput,\r\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\r\n 'cide-element-input-label-start': (labelDir === 'start'),\r\n 'cide-element-input-label-end': (labelDir === 'end'),\r\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\r\n 'cide-element-input-label-less': (!label || labelHide),\r\n 'cide-element-style-outline': (fill === 'outline'),\r\n 'cide-element-style-solid': (fill === 'solid'),\r\n 'cide-element-style-standard': (fill === 'standard'),\r\n 'cide-element-input-number': (type === 'number')\r\n }\">\r\n <!-- label -->\r\n @if (label && !labelHide) {\r\n <label [for]=\"id\" class=\"cide-input-label\">\r\n {{label}}\r\n @if (required) {\r\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <!-- all one line elemets which dose not affect with label and error text -->\r\n <div class=\"cide-element-input-wrapper\">\r\n <!-- Leading Icon -->\r\n @if (leadingIcon) {\r\n <span class=\"cide-input-leading-icon-wrapper\">\r\n <span\r\n class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\r\n </span>\r\n }\r\n\r\n <!-- Trailing icon -->\r\n @if (trailingIconInternal) {\r\n <span class=\"tw-absolute cide-input-trailing-icon tw-select-none tw-right-0\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\r\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\r\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\r\n </span>\r\n }\r\n\r\n <!-- Clear -->\r\n @if (clearInput && ngModel) {\r\n <button class=\"cide-input-clear\" (click)=\"ClearInputValue()\">\r\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\r\n </button>\r\n }\r\n\r\n <!-- Date Input Wrapper -->\r\n @if (type === 'date') {\r\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\r\n <!-- Date Input (read-only to prevent manual input) -->\r\n <input [placeholder]=\"placeholder\" [id]=\"id || idRandom\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [value]=\"getDateDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\r\n \r\n <!-- Placeholder overlay for empty date -->\r\n @if (!ngModel && placeholder) {\r\n <div class=\"cide-input-date-overlay\">\r\n {{placeholder}}\r\n </div>\r\n }\r\n\r\n <!-- Date picker is now rendered as a portal appended to body -->\r\n </div>\r\n }\r\n\r\n <!-- Time Input Wrapper -->\r\n @if (isTimeType()) {\r\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\r\n <!-- Time Input (read-only to prevent manual input) -->\r\n <input [placeholder]=\"placeholder\" [id]=\"id || idRandom\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [value]=\"getTimeDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\r\n \r\n <!-- Placeholder overlay for empty time -->\r\n @if (!ngModel && placeholder) {\r\n <div class=\"cide-input-date-overlay\">\r\n {{placeholder}}\r\n </div>\r\n }\r\n\r\n <!-- Time picker is now rendered as a portal appended to body -->\r\n </div>\r\n }\r\n\r\n <!-- DateTime Local Input -->\r\n @if (type === 'datetime-local') {\r\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\r\n <!-- DateTime Input (read-only to prevent manual input) -->\r\n <input [placeholder]=\"placeholder\" [id]=\"id || idRandom\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [value]=\"getDateTimeDisplayValue()\" type=\"text\" readonly (focus)=\"focusControl()\" (click)=\"trailingIconClick()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none tw-cursor-pointer\" />\r\n \r\n <!-- Placeholder overlay for empty datetime -->\r\n @if (!ngModel && placeholder) {\r\n <div class=\"cide-input-date-overlay\">\r\n {{placeholder}}\r\n </div>\r\n }\r\n\r\n <!-- DateTime picker is now rendered as a portal appended to body -->\r\n </div>\r\n }\r\n\r\n <!-- Regular Input (non-date, non-time, non-datetime-local, non-url) -->\r\n @if (type !== 'date' && !isTimeType() && type !== 'datetime-local' && type !== 'url') {\r\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\r\n [autocomplete]=\"autocomplete\" [min]=\"min\" [max]=\"max\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\r\n }\r\n\r\n <!-- URL Input -->\r\n @if (type === 'url') {\r\n <input [placeholder]=\"placeholder\" [id]=\"id\" [disabled]=\"disabled\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n [(ngModel)]=\"ngModel\" [type]=\"typeInternal\" (input)=\"upDateValue($event)\" (focus)=\"focusControl()\"\r\n [autocomplete]=\"autocomplete\"\r\n class=\"tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 cide-input-input tw-outline-none\" />\r\n }\r\n </div>\r\n <!-- error text / helper text -->\r\n @if ((errorText || helperText || !helperTextCollapse) && !hideHelperAndErrorText) {\r\n <span class=\"cide-input-help-error-text\">{{\r\n isValid\r\n ? helperText : (errorText ?\r\n (isTouched ? errorText : helperText)\r\n : helperText)}}\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Input with tralling icon -->\r\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\r\n <div class=\"tw-w-fullh-full tw-relative\">\r\n <label\r\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\r\n <span class=\"tw-absolute -tw-bottom-px tw-right-0 -tw-z-10 tw-text-gray-400\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\r\n </span>\r\n <input\r\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-1 tw-pr-8 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\r\n value=\"Ankush Bhure\" />\r\n </div>\r\n </div> -->\r\n\r\n <!-- Input with leading icon -->\r\n <!-- <div class=\"tw-inline-block tw-h-12 tw-w-64\">\r\n <div class=\"tw-w-fullh-full tw-relative\">\r\n <label\r\n class=\"tw-absolute -tw-top-1/3 tw-mx-2 tw-bg-white tw-px-0.5 tw-py-0 tw-text-sm tw-leading-4 tw-text-gray-700\">Name</label>\r\n <span class=\"tw-absolute -tw-bottom-px tw-left-0 -tw-z-10 tw-text-gray-400\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"> person </span>\r\n </span>\r\n <input\r\n class=\"tw-m-0 tw-h-8 tw-w-full tw-overflow-hidden tw-rounded-md tw-border-2 tw-border-solid tw-border-gray-300 tw-bg-transparent tw-p-0 tw-pb-0.5 tw-pl-8 tw-pr-1 tw-text-sm tw-text-gray-600 tw-outline-none hover:tw-border-blue-400 focus:tw-border-blue-400 focus:tw-text-gray-950\"\r\n value=\"Ankush Bhure\" />\r\n </div>\r\n </div> -->\r\n\r\n <!------------------------------------------CHECKBOX------------------------------------------>\r\n @if (type === 'checkbox') {\r\n <div class=\"tw-flex\">\r\n <div class=\"cide-checkbox tw-relative\">\r\n <input [checked]=\"ngModel\" [value]=\"ngModel\" [id]=\"idRandom\" [type]=\"type\" [disabled]=\"disabled\"\r\n class=\"tw-absolute tw-left-0 tw-invisible\" (click)=\"updateValueCheckBox(!ngModel); focusControl()\"\r\n [autocomplete]=\"autocomplete\" />\r\n <label class=\"cide-checkbox-label tw-cursor-pointer\" [for]=\"idRandom\">\r\n <span class=\"tw-border-2 tw-border-solid tw-relative tw-rounded-md\">\r\n <svg width=\"12px\" height=\"10px\" class=\"tw-absolute\">\r\n <use xlink:href=\"#sdfwiorfklasfjjalfjwerwr\"></use>\r\n </svg>\r\n </span>\r\n @if (!labelHide) {\r\n <span class=\"tw-text-sm tw-pl-2 tw-leading-[18px] tw-select-none tw-cursor-pointer\">{{label}}</span>\r\n }\r\n </label>\r\n <svg class=\"tw-absolute tw-h-0 tw-w-0 tw-select-none tw-pointer-events-none\">\r\n <!-- Element hidden and its xpath is used to display inside SVG -->\r\n <symbol id=\"sdfwiorfklasfjjalfjwerwr\" viewbox=\"0 0 12 10\">\r\n <polyline points=\"1.5 6 4.5 9 10.5 1\"></polyline>\r\n </symbol>\r\n </svg>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-------------------------------------------SELECT------------------------------------------->\r\n @if (type === 'select') {\r\n <div>sas\r\n <div class=\"tw-relative\">\r\n <div class=\"tw-absolute\">\r\n @for (item of option; track $index) {\r\n <div class=\"tw-w-full\">\r\n {{item}}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Date Picker Template -->\r\n<ng-template #datePickerTemplate>\r\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4\">\r\n <!-- Date Picker Header -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-4\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_left</span>\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-text-sm tw-font-medium tw-text-gray-800 hover:tw-bg-gray-100 tw-px-3 tw-py-1 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n {{ monthNames[currentMonth] }} {{ currentYear }}\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_right</span>\r\n </button>\r\n </div>\r\n\r\n <!-- Month/Year Selector -->\r\n @if (showMonthYearSelector) {\r\n <div class=\"tw-mb-4\">\r\n <!-- Year Navigation -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-3\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_left</span>\r\n </button>\r\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-800\">{{ currentYear }}</span>\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_right</span>\r\n </button>\r\n </div>\r\n \r\n <!-- Month Grid -->\r\n <div class=\"tw-grid tw-grid-cols-3 tw-gap-2 tw-mb-3\">\r\n @for (monthName of shortMonthNames; track $index) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-rounded-md tw-transition-colors tw-text-center\"\r\n [class.tw-bg-blue-500]=\"$index === currentMonth\"\r\n [class.tw-text-white]=\"$index === currentMonth\"\r\n [class.tw-text-gray-700]=\"$index !== currentMonth\"\r\n [class.hover:tw-bg-blue-50]=\"$index !== currentMonth\"\r\n (click)=\"selectMonth($index)\">\r\n {{ monthName }}\r\n </button>\r\n }\r\n </div>\r\n \r\n <!-- Back to Calendar Button -->\r\n <div class=\"tw-text-center\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-sm tw-text-blue-600 hover:tw-bg-blue-50 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n Back to Calendar\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Calendar View -->\r\n <div>\r\n <!-- Days of Week Header -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1 tw-mb-2\">\r\n @for (dayName of ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; track dayName) {\r\n <div class=\"tw-text-center tw-text-xs tw-font-medium tw-text-gray-500 tw-py-2\">\r\n {{ dayName }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Calendar Days -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1\">\r\n @for (dayInfo of calendarDays; track dayInfo.date.getTime()) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-text-sm tw-rounded-md tw-transition-colors tw-flex tw-items-center tw-justify-center\"\r\n [class.tw-text-gray-400]=\"!dayInfo.isCurrentMonth\"\r\n [class.tw-text-gray-900]=\"dayInfo.isCurrentMonth\"\r\n [class.tw-bg-blue-500]=\"dayInfo.isSelected\"\r\n [class.tw-text-white]=\"dayInfo.isSelected\"\r\n [class.tw-bg-blue-100]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.tw-text-blue-800]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.hover:tw-bg-blue-50]=\"dayInfo.isCurrentMonth && !dayInfo.isSelected\"\r\n (click)=\"showDateTimePicker ? selectDateTimeDate(dayInfo) : selectDate(dayInfo)\">\r\n {{ dayInfo.day }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Date Picker Footer -->\r\n <div class=\"tw-flex tw-justify-between tw-mt-3 tw-pt-2 tw-border-t tw-border-gray-200\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-text-gray-600 hover:tw-bg-gray-100 tw-rounded tw-transition-colors\"\r\n (click)=\"closeDatePicker()\">\r\n Cancel\r\n </button>\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700 tw-rounded tw-transition-colors\"\r\n (click)=\"closeDatePicker()\">\r\n Done\r\n </button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<!-- Time Picker Template -->\r\n<ng-template #timePickerTemplate>\r\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4 tw-w-72\">\r\n <!-- Time Picker Header -->\r\n <div class=\"tw-text-center tw-mb-3\">\r\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-800 tw-m-0\">Time</h3>\r\n </div>\r\n\r\n <!-- Time Spinners Container -->\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-gap-3 tw-mb-4\">\r\n <!-- Hours -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-12 tw-text-center\">\r\n {{ timeHours }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">hour</span>\r\n </div>\r\n\r\n <!-- Colon Separator -->\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-mb-4\">:</div>\r\n\r\n <!-- Minutes -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-12 tw-text-center\">\r\n {{ timeMinutes.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">min</span>\r\n </div>\r\n\r\n <!-- Colon Separator -->\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-mb-4\">:</div>\r\n\r\n <!-- Seconds -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-2xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-12 tw-text-center\">\r\n {{ timeSeconds.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-lg\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">sec</span>\r\n </div>\r\n </div>\r\n\r\n <!-- AM/PM Toggle -->\r\n <div class=\"tw-flex tw-justify-center tw-mb-3\">\r\n <div class=\"tw-inline-flex tw-rounded-md tw-bg-gray-100 tw-p-0.5\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-4 tw-py-1.5 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'AM'\"\r\n [class.tw-shadow]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'AM'\"\r\n (click)=\"timeFormat = 'AM'\">\r\n AM\r\n </button>\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-4 tw-py-1.5 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'PM'\"\r\n [class.tw-shadow]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'PM'\"\r\n (click)=\"timeFormat = 'PM'\">\r\n PM\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Current Time Display -->\r\n <div class=\"tw-text-center tw-mb-3\">\r\n <span class=\"tw-text-sm tw-text-gray-600\">\r\n {{ timeHours }}:{{ timeMinutes.toString().padStart(2, '0') }}:{{ timeSeconds.toString().padStart(2, '0') }} {{ timeFormat }}\r\n </span>\r\n </div>\r\n\r\n <!-- Time Picker Footer -->\r\n <div class=\"tw-flex tw-gap-2\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-text-gray-700 tw-bg-white tw-border tw-border-gray-300 hover:tw-bg-gray-50 tw-rounded tw-transition-colors\"\r\n (click)=\"showDateTimePicker ? closeDateTimePicker() : closeTimePicker()\">\r\n Cancel\r\n </button>\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700 tw-rounded tw-transition-colors tw-border-0\"\r\n (click)=\"showDateTimePicker ? applyDateTime() : applyTime()\">\r\n OK\r\n </button>\r\n </div>\r\n\r\n </div>\r\n</ng-template>\r\n\r\n<!-- DateTime Picker Template (Combined Date + Time) -->\r\n<ng-template #dateTimePickerTemplate>\r\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-lg tw-shadow-lg tw-p-4\">\r\n <!-- DateTime Picker Header -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-4\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_left</span>\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-text-sm tw-font-medium tw-text-gray-800 hover:tw-bg-gray-100 tw-px-3 tw-py-1 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n {{ monthNames[currentMonth] }} {{ currentYear }}\r\n </button>\r\n \r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextMonth()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">chevron_right</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"tw-grid tw-grid-cols-2 tw-gap-4\">\r\n <!-- Left Side: Calendar -->\r\n <div>\r\n <!-- Month/Year Selector -->\r\n @if (showMonthYearSelector) {\r\n <div class=\"tw-mb-4\">\r\n <!-- Year Navigation -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-3\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"previousYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_left</span>\r\n </button>\r\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-800\">{{ currentYear }}</span>\r\n <button \r\n type=\"button\" \r\n class=\"tw-p-1 tw-rounded-md tw-text-gray-600 hover:tw-bg-gray-100 tw-transition-colors\"\r\n (click)=\"nextYear()\">\r\n <span class=\"material-symbols-outlined tw-text-base\">keyboard_double_arrow_right</span>\r\n </button>\r\n </div>\r\n \r\n <!-- Month Grid -->\r\n <div class=\"tw-grid tw-grid-cols-3 tw-gap-2 tw-mb-3\">\r\n @for (monthName of shortMonthNames; track $index) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-2 tw-py-1.5 tw-text-xs tw-rounded-md tw-transition-colors tw-text-center\"\r\n [class.tw-bg-blue-500]=\"$index === currentMonth\"\r\n [class.tw-text-white]=\"$index === currentMonth\"\r\n [class.tw-text-gray-700]=\"$index !== currentMonth\"\r\n [class.hover:tw-bg-blue-50]=\"$index !== currentMonth\"\r\n (click)=\"selectMonth($index)\">\r\n {{ monthName }}\r\n </button>\r\n }\r\n </div>\r\n \r\n <!-- Back to Calendar Button -->\r\n <div class=\"tw-text-center\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-text-blue-600 hover:tw-bg-blue-50 tw-rounded-md tw-transition-colors\"\r\n (click)=\"toggleMonthYearSelector()\">\r\n Back to Calendar\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Calendar View -->\r\n <div>\r\n <!-- Days of Week Header -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1 tw-mb-2\">\r\n @for (dayName of ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; track dayName) {\r\n <div class=\"tw-text-center tw-text-xs tw-font-medium tw-text-gray-500 tw-py-1\">\r\n {{ dayName }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Calendar Days -->\r\n <div class=\"tw-grid tw-grid-cols-7 tw-gap-1\">\r\n @for (dayInfo of calendarDays; track dayInfo.date.getTime()) {\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-text-xs tw-rounded-md tw-transition-colors tw-flex tw-items-center tw-justify-center\"\r\n [class.tw-text-gray-400]=\"!dayInfo.isCurrentMonth\"\r\n [class.tw-text-gray-900]=\"dayInfo.isCurrentMonth\"\r\n [class.tw-bg-blue-500]=\"dayInfo.isSelected\"\r\n [class.tw-text-white]=\"dayInfo.isSelected\"\r\n [class.tw-bg-blue-100]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.tw-text-blue-800]=\"dayInfo.isToday && !dayInfo.isSelected\"\r\n [class.hover:tw-bg-blue-50]=\"dayInfo.isCurrentMonth && !dayInfo.isSelected\"\r\n (click)=\"selectDateTimeDate(dayInfo)\">\r\n {{ dayInfo.day }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Right Side: Time Picker -->\r\n <div class=\"tw-border-l tw-border-gray-200 tw-pl-4\">\r\n <div class=\"tw-text-center tw-mb-2\">\r\n <h4 class=\"tw-text-sm tw-font-semibold tw-text-gray-800 tw-m-0\">Time</h4>\r\n </div>\r\n\r\n <!-- Time Spinners -->\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-gap-2 tw-mb-3\">\r\n <!-- Hours -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-10 tw-text-center\">\r\n {{ timeHours }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('hours', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">hour</span>\r\n </div>\r\n\r\n <!-- Colon -->\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-mb-3\">:</div>\r\n\r\n <!-- Minutes -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-10 tw-text-center\">\r\n {{ timeMinutes.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('minutes', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">min</span>\r\n </div>\r\n\r\n <!-- Colon -->\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-mb-3\">:</div>\r\n\r\n <!-- Seconds -->\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'up')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_less</span>\r\n </button>\r\n <div class=\"tw-text-xl tw-font-medium tw-text-gray-800 tw-my-1 tw-w-10 tw-text-center\">\r\n {{ timeSeconds.toString().padStart(2, '0') }}\r\n </div>\r\n <button \r\n type=\"button\"\r\n class=\"tw-w-7 tw-h-7 tw-flex tw-items-center tw-justify-center tw-rounded-full tw-text-gray-500 hover:tw-bg-gray-100 tw-transition-colors tw-border-0 tw-bg-white\"\r\n (click)=\"adjustTime('seconds', 'down')\">\r\n <span class=\"material-symbols-outlined tw-text-base\">expand_more</span>\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">sec</span>\r\n </div>\r\n </div>\r\n\r\n <!-- AM/PM Toggle -->\r\n <div class=\"tw-flex tw-justify-center tw-mb-2\">\r\n <div class=\"tw-inline-flex tw-rounded-md tw-bg-gray-100 tw-p-0.5\">\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'AM'\"\r\n [class.tw-shadow-sm]=\"timeFormat === 'AM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'AM'\"\r\n (click)=\"timeFormat = 'AM'\">\r\n AM\r\n </button>\r\n <button \r\n type=\"button\"\r\n class=\"tw-px-3 tw-py-1 tw-text-xs tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\r\n [class.tw-bg-white]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-800]=\"timeFormat === 'PM'\"\r\n [class.tw-shadow-sm]=\"timeFormat === 'PM'\"\r\n [class.tw-text-gray-600]=\"timeFormat !== 'PM'\"\r\n (click)=\"timeFormat = 'PM'\">\r\n PM\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Current Time Display -->\r\n <div class=\"tw-text-center tw-text-xs tw-text-gray-600\">\r\n {{ timeHours }}:{{ timeMinutes.toString().padStart(2, '0') }}:{{ timeSeconds.toString().padStart(2, '0') }} {{ timeFormat }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DateTime Picker Footer -->\r\n <div class=\"tw-flex tw-gap-2 tw-mt-3 tw-pt-2 tw-border-t tw-border-gray-200\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-text-gray-700 tw-bg-white tw-border tw-border-gray-300 hover:tw-bg-gray-50 tw-rounded tw-transition-colors\"\r\n (click)=\"closeDateTimePicker()\">\r\n Cancel\r\n </button>\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700 tw-rounded tw-transition-colors tw-border-0\"\r\n (click)=\"applyDateTime()\">\r\n OK\r\n </button>\r\n </div>\r\n </div>\r\n</ng-template>", styles: ["input[type=date]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=date]::-webkit-inner-spin-button,input[type=date]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=date]{-moz-appearance:textfield}input[type=date]::-webkit-datetime-edit,input[type=date]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=date]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=date]{background:transparent!important;border:none!important;outline:none!important}.cide-input-date-wrapper{position:relative;width:100%}.cide-input-date-overlay{position:absolute;inset:0;pointer-events:none;display:flex;align-items:center;padding-left:.25rem;color:#6b7280}.cide-input-date-has-value .cide-input-date-overlay{display:none}.cide-date-picker-panel{min-width:320px;max-width:400px;z-index:1000}.cide-date-picker-panel{animation:datePickerFadeIn .2s ease-out}@keyframes datePickerFadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.cide-date-picker-panel button:disabled{opacity:.5;cursor:not-allowed}.cide-date-picker-panel button:focus{outline:2px solid rgb(59,130,246);outline-offset:2px}.cide-date-picker-panel button:hover:not(:disabled){transform:scale(1.05);transition:transform .1s ease-in-out}.cide-date-picker-panel button.today:not(.selected){box-shadow:0 0 0 1px #3b82f6}.cide-date-picker-panel button.selected{box-shadow:0 2px 4px #3b82f64d;font-weight:600}.cide-date-picker-panel .nav-button{transition:all .2s ease-in-out}.cide-date-picker-panel .nav-button:hover{background-color:#f3f4f6;transform:scale(1.1)}.cide-date-picker-panel h3{-webkit-user-select:none;user-select:none;transition:color .2s ease-in-out}.cide-date-picker-panel .days-header{border-bottom:1px solid rgb(229,231,235);margin-bottom:.5rem;padding-bottom:.5rem}.cide-date-picker-portal{box-shadow:0 10px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:.5rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out}.cide-date-picker-portal{animation:portalFadeIn .2s ease-out}@keyframes portalFadeIn{0%{opacity:0;transform:translateY(-8px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.cide-date-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}input[type=time]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=time]::-webkit-inner-spin-button,input[type=time]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=time]{-moz-appearance:textfield}input[type=time]::-webkit-datetime-edit,input[type=time]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=time]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=time]{background:transparent!important;border:none!important;outline:none!important}.cide-time-picker-portal{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:1rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out;animation:portalFadeIn .2s ease-out}.cide-time-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}input[type=datetime-local]::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}input[type=datetime-local]::-webkit-inner-spin-button,input[type=datetime-local]::-webkit-outer-spin-button{display:none;-webkit-appearance:none}input[type=datetime-local]{-moz-appearance:textfield}input[type=datetime-local]::-webkit-datetime-edit,input[type=datetime-local]::-webkit-datetime-edit-fields-wrapper{padding:0;background:transparent}input[type=datetime-local]::-webkit-datetime-edit-text{color:transparent;background:transparent}input[type=datetime-local]{background:transparent!important;border:none!important;outline:none!important}.cide-datetime-picker-portal{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;border-radius:1rem;background:#fff;border:1px solid rgb(229,231,235);z-index:9999;transition:opacity .2s ease-in-out,transform .2s ease-in-out;animation:portalFadeIn .2s ease-out}.cide-datetime-picker-portal.cide-portal{position:fixed!important;z-index:9999!important}\n"] }]
|
|
1239
1671
|
}], ctorParameters: () => [], propDecorators: { fill: [{
|
|
1240
1672
|
type: Input
|
|
1241
1673
|
}], label: [{
|
|
@@ -1295,6 +1727,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
1295
1727
|
}], datePickerTemplate: [{
|
|
1296
1728
|
type: ViewChild,
|
|
1297
1729
|
args: ['datePickerTemplate', { static: true }]
|
|
1730
|
+
}], timePickerTemplate: [{
|
|
1731
|
+
type: ViewChild,
|
|
1732
|
+
args: ['timePickerTemplate', { static: true }]
|
|
1733
|
+
}], dateTimePickerTemplate: [{
|
|
1734
|
+
type: ViewChild,
|
|
1735
|
+
args: ['dateTimePickerTemplate', { static: true }]
|
|
1298
1736
|
}] } });
|
|
1299
1737
|
|
|
1300
1738
|
class CideSelectOptionComponent {
|
|
@@ -2136,7 +2574,7 @@ class CideSelectComponent {
|
|
|
2136
2574
|
multi: true,
|
|
2137
2575
|
useExisting: forwardRef(() => CideSelectComponent),
|
|
2138
2576
|
}
|
|
2139
|
-
], queries: [{ propertyName: "optionComponents", predicate: CideSelectOptionComponent }], viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "dropdownTemplate", first: true, predicate: ["dropdownTemplate"], descendants: true }, { propertyName: "dropdownContainer", first: true, predicate: ["dropdownContainer"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-select\" [ngClass]=\"{\n 'cide-element-size-xxs': (size === '2xs'),\n 'cide-element-size-xs': (size === 'xs'),\n 'cide-element-size-sm': (size === 'sm'),\n 'cide-element-size-md': (size === 'md'),\n 'cide-element-size-lg': (size === 'lg'),\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\n 'cide-element-input-label-start': (labelDir === 'start'),\n 'cide-element-input-label-end': (labelDir === 'end'),\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\n 'cide-element-input-label-less': (!label || labelHide),\n 'cide-element-style-outline': (fill === 'outline'),\n 'cide-element-style-solid': (fill === 'solid'),\n 'cide-element-style-standard': (fill === 'standard'),\n}\">\n @if (label && !labelHide) {\n <label [for]=\"id\" class=\"cide-select-label\">\n {{ label }}\n @if (required) {\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\n }\n </label>\n }\n\n <div class=\"cide-element-select-wrapper tw-relative\">\n <!-- Leading Icon -->\n @if (leadingIcon) {\n <span class=\"cide-input-leading-icon-wrapper\">\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\n </span>\n }\n\n <!-- Select Button -->\n <button type=\"button\" [id]=\"id\" [disabled]=\"disabled\" (click)=\"toggleDropdown()\" (blur)=\"onBlur()\" [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIcon || clearInput || loading,\n 'tw-pr-1': !trailingIcon && !clearInput && !loading,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled,\n 'cide-select-button tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 tw-outline-none tw-text-left tw-cursor-pointer': true\n }\" class=\"cide-select-button\">\n\n <span class=\"cide-select-value\" [ngClass]=\"{'tw-text-gray-400': !ngModel}\">\n {{ ngModel ? getSelectedOptionLabel() : placeholder }}\n </span>\n </button>\n\n <!-- Trailing Icon (Dropdown Arrow or Loading Spinner) -->\n @if (!clearInput || !ngModel || loading) {\n <span class=\"tw-absolute tw-top-1/2 tw-right-0 tw-transform -tw-translate-y-1/2 tw-select-none tw-z-10\">\n <!-- Loading Spinner -->\n @if (loading) {\n <span class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-500\">\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\n </path>\n </svg>\n </span>\n }\n <!-- Dropdown Arrow -->\n @if (!loading) {\n <span\n class=\"material-symbols-outlined tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center !tw-text-2xl tw-transition-transform tw-text-gray-500\"\n [ngClass]=\"{'tw-rotate-180': isOpen}\">\n expand_more\n </span>\n }\n </span>\n }\n\n <!-- Clear Button -->\n @if (clearInput && ngModel) {\n <button class=\"cide-input-clear\" (click)=\"clearSelection()\" type=\"button\">\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\n </button>\n }\n\n\n </div>\n\n <!-- Error/Helper Text -->\n @if ((errorText || helperText) && !isValid) {\n <span class=\"cide-select-help-error-text\">\n {{ errorText || helperText }}\n </span>\n }\n @if (helperText && isValid) {\n <span class=\"cide-select-help-error-text\">\n {{ helperText }}\n </span>\n }\n\n <!-- Portal Container -->\n <div #dropdownContainer></div>\n</div>\n\n<!-- Portal Template for Dropdown -->\n<ng-template #dropdownTemplate let-context=\"context\">\n <div\n class=\"cide-select-dropdown-portal tw-bg-white tw-border tw-border-gray-300 tw-rounded-md tw-shadow-lg tw-max-h-60 tw-overflow-y-auto tw-z-50\">\n <!-- Search Input (if searchable and showSearchInput is true) -->\n @if (context?.searchable && showSearchInput) {\n <div class=\"tw-p-2 tw-border-b tw-border-gray-200\">\n <input #searchInput type=\"text\" placeholder=\"Search options...\" [value]=\"context?.searchTerm\"\n (input)=\"context?.onSearchInput($event)\" (mousedown)=\"onDropdownMouseDown()\" (focus)=\"onDropdownMouseDown()\"\n (click)=\"$event.stopPropagation()\" (blur)=\"onSearchInputBlur($event)\"\n class=\"tw-w-full tw-px-2 tw-py-1 tw-text-sm tw-border tw-border-gray-300 tw-rounded tw-outline-none focus:tw-border-blue-500\">\n </div>\n }\n\n <!-- Options List -->\n <div class=\"tw-py-1\">\n <!-- Loading State -->\n @if (context?.loading) {\n <div class=\"tw-px-3 tw-py-4 tw-text-center\">\n <div class=\"tw-flex tw-items-center tw-justify-center tw-space-x-2\">\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4 tw-text-gray-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\"\n viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\n </path>\n </svg>\n <span class=\"tw-text-sm tw-text-gray-500\">Loading...</span>\n </div>\n </div>\n }\n <!-- Options -->\n @if (!context?.loading) {\n @for (option of context?.options; track $index) {\n <button type=\"button\" (mousedown)=\"onDropdownMouseDown()\" (click)=\"context?.selectOption(option)\"\n (keyup.enter)=\"context?.selectOption(option)\" (keyup.space)=\"context?.selectOption(option)\"\n [disabled]=\"option.disabled\" [ngClass]=\"{\n 'cide-select-option tw-w-full tw-text-left tw-px-3 tw-py-2 tw-text-sm tw-cursor-pointer tw-transition-colors hover:tw-bg-gray-100 tw-border-none tw-bg-transparent tw-outline-none': true,\n 'tw-bg-blue-50 tw-text-blue-700': context?.isOptionSelected(option),\n 'tw-text-gray-400 tw-cursor-not-allowed': option.disabled\n }\" class=\"cide-select-option\">\n {{ context?.getOptionLabel(option) }}\n </button>\n }\n }\n\n <!-- No options message -->\n @if (!context?.loading && context?.options.length === 0) {\n <div class=\"tw-px-3 tw-py-2 tw-text-sm tw-text-gray-500 tw-italic\">\n {{ context?.searchable && context?.searchTerm ? 'No options found' : 'No options available' }}\n </div>\n }\n </div>\n </div>\n</ng-template>", styles: [".cide-select{position:relative;width:100%}.cide-select .cide-select-button{display:flex;align-items:center;justify-content:space-between;width:100%;background-color:transparent;transition:all .2s ease-in-out;cursor:pointer;outline:none}.cide-select .cide-select-button:disabled{cursor:not-allowed}.cide-select .cide-select-value{flex:1;text-align:left}.cide-select .cide-select-dropdown{position:absolute;left:0;right:0;z-index:1000;background-color:#fff;border:1px solid var(--cide-input-border);border-radius:.375rem;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;max-height:15rem;overflow-y:auto}.cide-select .cide-select-dropdown .cide-select-option{display:block;width:100%;text-align:left;padding:.5rem .75rem;font-size:.875rem;cursor:pointer;transition:background-color .15s ease-in-out;border:none;background:transparent;outline:none}.cide-select .cide-select-dropdown .cide-select-option:hover:not(:disabled){background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:focus{background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:disabled{cursor:not-allowed}.cide-select-dropdown-portal{width:auto!important;min-width:200px;max-width:80vw;word-wrap:break-word;overflow-wrap:break-word}.cide-select-dropdown-portal .tw-py-1{min-width:0}.cide-select-dropdown-portal .cide-select-option{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }] });
|
|
2577
|
+
], queries: [{ propertyName: "optionComponents", predicate: CideSelectOptionComponent }], viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "dropdownTemplate", first: true, predicate: ["dropdownTemplate"], descendants: true }, { propertyName: "dropdownContainer", first: true, predicate: ["dropdownContainer"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-select\" [ngClass]=\"{\r\n 'cide-element-size-xxs': (size === '2xs'),\r\n 'cide-element-size-xs': (size === 'xs'),\r\n 'cide-element-size-sm': (size === 'sm'),\r\n 'cide-element-size-md': (size === 'md'),\r\n 'cide-element-size-lg': (size === 'lg'),\r\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\r\n 'cide-element-input-label-start': (labelDir === 'start'),\r\n 'cide-element-input-label-end': (labelDir === 'end'),\r\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\r\n 'cide-element-input-label-less': (!label || labelHide),\r\n 'cide-element-style-outline': (fill === 'outline'),\r\n 'cide-element-style-solid': (fill === 'solid'),\r\n 'cide-element-style-standard': (fill === 'standard'),\r\n}\">\r\n @if (label && !labelHide) {\r\n <label [for]=\"id\" class=\"cide-select-label\">\r\n {{ label }}\r\n @if (required) {\r\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"cide-element-select-wrapper tw-relative\">\r\n <!-- Leading Icon -->\r\n @if (leadingIcon) {\r\n <span class=\"cide-input-leading-icon-wrapper\">\r\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\r\n </span>\r\n }\r\n\r\n <!-- Select Button -->\r\n <button type=\"button\" [id]=\"id\" [disabled]=\"disabled\" (click)=\"toggleDropdown()\" (blur)=\"onBlur()\" [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIcon || clearInput || loading,\r\n 'tw-pr-1': !trailingIcon && !clearInput && !loading,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled,\r\n 'cide-select-button tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 tw-outline-none tw-text-left tw-cursor-pointer': true\r\n }\" class=\"cide-select-button\">\r\n\r\n <span class=\"cide-select-value\" [ngClass]=\"{'tw-text-gray-400': !ngModel}\">\r\n {{ ngModel ? getSelectedOptionLabel() : placeholder }}\r\n </span>\r\n </button>\r\n\r\n <!-- Trailing Icon (Dropdown Arrow or Loading Spinner) -->\r\n @if (!clearInput || !ngModel || loading) {\r\n <span class=\"tw-absolute tw-top-1/2 tw-right-0 tw-transform -tw-translate-y-1/2 tw-select-none tw-z-10\">\r\n <!-- Loading Spinner -->\r\n @if (loading) {\r\n <span class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-500\">\r\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\r\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\r\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\r\n </path>\r\n </svg>\r\n </span>\r\n }\r\n <!-- Dropdown Arrow -->\r\n @if (!loading) {\r\n <span\r\n class=\"material-symbols-outlined tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center !tw-text-2xl tw-transition-transform tw-text-gray-500\"\r\n [ngClass]=\"{'tw-rotate-180': isOpen}\">\r\n expand_more\r\n </span>\r\n }\r\n </span>\r\n }\r\n\r\n <!-- Clear Button -->\r\n @if (clearInput && ngModel) {\r\n <button class=\"cide-input-clear\" (click)=\"clearSelection()\" type=\"button\">\r\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\r\n </button>\r\n }\r\n\r\n\r\n </div>\r\n\r\n <!-- Error/Helper Text -->\r\n @if ((errorText || helperText) && !isValid) {\r\n <span class=\"cide-select-help-error-text\">\r\n {{ errorText || helperText }}\r\n </span>\r\n }\r\n @if (helperText && isValid) {\r\n <span class=\"cide-select-help-error-text\">\r\n {{ helperText }}\r\n </span>\r\n }\r\n\r\n <!-- Portal Container -->\r\n <div #dropdownContainer></div>\r\n</div>\r\n\r\n<!-- Portal Template for Dropdown -->\r\n<ng-template #dropdownTemplate let-context=\"context\">\r\n <div\r\n class=\"cide-select-dropdown-portal tw-bg-white tw-border tw-border-gray-300 tw-rounded-md tw-shadow-lg tw-max-h-60 tw-overflow-y-auto tw-z-50\">\r\n <!-- Search Input (if searchable and showSearchInput is true) -->\r\n @if (context?.searchable && showSearchInput) {\r\n <div class=\"tw-p-2 tw-border-b tw-border-gray-200\">\r\n <input #searchInput type=\"text\" placeholder=\"Search options...\" [value]=\"context?.searchTerm\"\r\n (input)=\"context?.onSearchInput($event)\" (mousedown)=\"onDropdownMouseDown()\" (focus)=\"onDropdownMouseDown()\"\r\n (click)=\"$event.stopPropagation()\" (blur)=\"onSearchInputBlur($event)\"\r\n class=\"tw-w-full tw-px-2 tw-py-1 tw-text-sm tw-border tw-border-gray-300 tw-rounded tw-outline-none focus:tw-border-blue-500\">\r\n </div>\r\n }\r\n\r\n <!-- Options List -->\r\n <div class=\"tw-py-1\">\r\n <!-- Loading State -->\r\n @if (context?.loading) {\r\n <div class=\"tw-px-3 tw-py-4 tw-text-center\">\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-space-x-2\">\r\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4 tw-text-gray-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\r\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\r\n </path>\r\n </svg>\r\n <span class=\"tw-text-sm tw-text-gray-500\">Loading...</span>\r\n </div>\r\n </div>\r\n }\r\n <!-- Options -->\r\n @if (!context?.loading) {\r\n @for (option of context?.options; track $index) {\r\n <button type=\"button\" (mousedown)=\"onDropdownMouseDown()\" (click)=\"context?.selectOption(option)\"\r\n (keyup.enter)=\"context?.selectOption(option)\" (keyup.space)=\"context?.selectOption(option)\"\r\n [disabled]=\"option.disabled\" [ngClass]=\"{\r\n 'cide-select-option tw-w-full tw-text-left tw-px-3 tw-py-2 tw-text-sm tw-cursor-pointer tw-transition-colors hover:tw-bg-gray-100 tw-border-none tw-bg-transparent tw-outline-none': true,\r\n 'tw-bg-blue-50 tw-text-blue-700': context?.isOptionSelected(option),\r\n 'tw-text-gray-400 tw-cursor-not-allowed': option.disabled\r\n }\" class=\"cide-select-option\">\r\n {{ context?.getOptionLabel(option) }}\r\n </button>\r\n }\r\n }\r\n\r\n <!-- No options message -->\r\n @if (!context?.loading && context?.options.length === 0) {\r\n <div class=\"tw-px-3 tw-py-2 tw-text-sm tw-text-gray-500 tw-italic\">\r\n {{ context?.searchable && context?.searchTerm ? 'No options found' : 'No options available' }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</ng-template>", styles: [".cide-select{position:relative;width:100%}.cide-select .cide-select-button{display:flex;align-items:center;justify-content:space-between;width:100%;background-color:transparent;transition:all .2s ease-in-out;cursor:pointer;outline:none}.cide-select .cide-select-button:disabled{cursor:not-allowed}.cide-select .cide-select-value{flex:1;text-align:left}.cide-select .cide-select-dropdown{position:absolute;left:0;right:0;z-index:1000;background-color:#fff;border:1px solid var(--cide-input-border);border-radius:.375rem;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;max-height:15rem;overflow-y:auto}.cide-select .cide-select-dropdown .cide-select-option{display:block;width:100%;text-align:left;padding:.5rem .75rem;font-size:.875rem;cursor:pointer;transition:background-color .15s ease-in-out;border:none;background:transparent;outline:none}.cide-select .cide-select-dropdown .cide-select-option:hover:not(:disabled){background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:focus{background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:disabled{cursor:not-allowed}.cide-select-dropdown-portal{width:auto!important;min-width:200px;max-width:80vw;word-wrap:break-word;overflow-wrap:break-word}.cide-select-dropdown-portal .tw-py-1{min-width:0}.cide-select-dropdown-portal .cide-select-option{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }] });
|
|
2140
2578
|
}
|
|
2141
2579
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSelectComponent, decorators: [{
|
|
2142
2580
|
type: Component,
|
|
@@ -2151,7 +2589,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
2151
2589
|
multi: true,
|
|
2152
2590
|
useExisting: forwardRef(() => CideSelectComponent),
|
|
2153
2591
|
}
|
|
2154
|
-
], template: "<div class=\"cide-select\" [ngClass]=\"{\n 'cide-element-size-xxs': (size === '2xs'),\n 'cide-element-size-xs': (size === 'xs'),\n 'cide-element-size-sm': (size === 'sm'),\n 'cide-element-size-md': (size === 'md'),\n 'cide-element-size-lg': (size === 'lg'),\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\n 'cide-element-input-label-start': (labelDir === 'start'),\n 'cide-element-input-label-end': (labelDir === 'end'),\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\n 'cide-element-input-label-less': (!label || labelHide),\n 'cide-element-style-outline': (fill === 'outline'),\n 'cide-element-style-solid': (fill === 'solid'),\n 'cide-element-style-standard': (fill === 'standard'),\n}\">\n @if (label && !labelHide) {\n <label [for]=\"id\" class=\"cide-select-label\">\n {{ label }}\n @if (required) {\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\n }\n </label>\n }\n\n <div class=\"cide-element-select-wrapper tw-relative\">\n <!-- Leading Icon -->\n @if (leadingIcon) {\n <span class=\"cide-input-leading-icon-wrapper\">\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\n </span>\n }\n\n <!-- Select Button -->\n <button type=\"button\" [id]=\"id\" [disabled]=\"disabled\" (click)=\"toggleDropdown()\" (blur)=\"onBlur()\" [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIcon || clearInput || loading,\n 'tw-pr-1': !trailingIcon && !clearInput && !loading,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n 'tw-h-7': size === 'sm',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled,\n 'cide-select-button tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 tw-outline-none tw-text-left tw-cursor-pointer': true\n }\" class=\"cide-select-button\">\n\n <span class=\"cide-select-value\" [ngClass]=\"{'tw-text-gray-400': !ngModel}\">\n {{ ngModel ? getSelectedOptionLabel() : placeholder }}\n </span>\n </button>\n\n <!-- Trailing Icon (Dropdown Arrow or Loading Spinner) -->\n @if (!clearInput || !ngModel || loading) {\n <span class=\"tw-absolute tw-top-1/2 tw-right-0 tw-transform -tw-translate-y-1/2 tw-select-none tw-z-10\">\n <!-- Loading Spinner -->\n @if (loading) {\n <span class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-500\">\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\n </path>\n </svg>\n </span>\n }\n <!-- Dropdown Arrow -->\n @if (!loading) {\n <span\n class=\"material-symbols-outlined tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center !tw-text-2xl tw-transition-transform tw-text-gray-500\"\n [ngClass]=\"{'tw-rotate-180': isOpen}\">\n expand_more\n </span>\n }\n </span>\n }\n\n <!-- Clear Button -->\n @if (clearInput && ngModel) {\n <button class=\"cide-input-clear\" (click)=\"clearSelection()\" type=\"button\">\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\n </button>\n }\n\n\n </div>\n\n <!-- Error/Helper Text -->\n @if ((errorText || helperText) && !isValid) {\n <span class=\"cide-select-help-error-text\">\n {{ errorText || helperText }}\n </span>\n }\n @if (helperText && isValid) {\n <span class=\"cide-select-help-error-text\">\n {{ helperText }}\n </span>\n }\n\n <!-- Portal Container -->\n <div #dropdownContainer></div>\n</div>\n\n<!-- Portal Template for Dropdown -->\n<ng-template #dropdownTemplate let-context=\"context\">\n <div\n class=\"cide-select-dropdown-portal tw-bg-white tw-border tw-border-gray-300 tw-rounded-md tw-shadow-lg tw-max-h-60 tw-overflow-y-auto tw-z-50\">\n <!-- Search Input (if searchable and showSearchInput is true) -->\n @if (context?.searchable && showSearchInput) {\n <div class=\"tw-p-2 tw-border-b tw-border-gray-200\">\n <input #searchInput type=\"text\" placeholder=\"Search options...\" [value]=\"context?.searchTerm\"\n (input)=\"context?.onSearchInput($event)\" (mousedown)=\"onDropdownMouseDown()\" (focus)=\"onDropdownMouseDown()\"\n (click)=\"$event.stopPropagation()\" (blur)=\"onSearchInputBlur($event)\"\n class=\"tw-w-full tw-px-2 tw-py-1 tw-text-sm tw-border tw-border-gray-300 tw-rounded tw-outline-none focus:tw-border-blue-500\">\n </div>\n }\n\n <!-- Options List -->\n <div class=\"tw-py-1\">\n <!-- Loading State -->\n @if (context?.loading) {\n <div class=\"tw-px-3 tw-py-4 tw-text-center\">\n <div class=\"tw-flex tw-items-center tw-justify-center tw-space-x-2\">\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4 tw-text-gray-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\"\n viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\n </path>\n </svg>\n <span class=\"tw-text-sm tw-text-gray-500\">Loading...</span>\n </div>\n </div>\n }\n <!-- Options -->\n @if (!context?.loading) {\n @for (option of context?.options; track $index) {\n <button type=\"button\" (mousedown)=\"onDropdownMouseDown()\" (click)=\"context?.selectOption(option)\"\n (keyup.enter)=\"context?.selectOption(option)\" (keyup.space)=\"context?.selectOption(option)\"\n [disabled]=\"option.disabled\" [ngClass]=\"{\n 'cide-select-option tw-w-full tw-text-left tw-px-3 tw-py-2 tw-text-sm tw-cursor-pointer tw-transition-colors hover:tw-bg-gray-100 tw-border-none tw-bg-transparent tw-outline-none': true,\n 'tw-bg-blue-50 tw-text-blue-700': context?.isOptionSelected(option),\n 'tw-text-gray-400 tw-cursor-not-allowed': option.disabled\n }\" class=\"cide-select-option\">\n {{ context?.getOptionLabel(option) }}\n </button>\n }\n }\n\n <!-- No options message -->\n @if (!context?.loading && context?.options.length === 0) {\n <div class=\"tw-px-3 tw-py-2 tw-text-sm tw-text-gray-500 tw-italic\">\n {{ context?.searchable && context?.searchTerm ? 'No options found' : 'No options available' }}\n </div>\n }\n </div>\n </div>\n</ng-template>", styles: [".cide-select{position:relative;width:100%}.cide-select .cide-select-button{display:flex;align-items:center;justify-content:space-between;width:100%;background-color:transparent;transition:all .2s ease-in-out;cursor:pointer;outline:none}.cide-select .cide-select-button:disabled{cursor:not-allowed}.cide-select .cide-select-value{flex:1;text-align:left}.cide-select .cide-select-dropdown{position:absolute;left:0;right:0;z-index:1000;background-color:#fff;border:1px solid var(--cide-input-border);border-radius:.375rem;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;max-height:15rem;overflow-y:auto}.cide-select .cide-select-dropdown .cide-select-option{display:block;width:100%;text-align:left;padding:.5rem .75rem;font-size:.875rem;cursor:pointer;transition:background-color .15s ease-in-out;border:none;background:transparent;outline:none}.cide-select .cide-select-dropdown .cide-select-option:hover:not(:disabled){background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:focus{background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:disabled{cursor:not-allowed}.cide-select-dropdown-portal{width:auto!important;min-width:200px;max-width:80vw;word-wrap:break-word;overflow-wrap:break-word}.cide-select-dropdown-portal .tw-py-1{min-width:0}.cide-select-dropdown-portal .cide-select-option{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"] }]
|
|
2592
|
+
], template: "<div class=\"cide-select\" [ngClass]=\"{\r\n 'cide-element-size-xxs': (size === '2xs'),\r\n 'cide-element-size-xs': (size === 'xs'),\r\n 'cide-element-size-sm': (size === 'sm'),\r\n 'cide-element-size-md': (size === 'md'),\r\n 'cide-element-size-lg': (size === 'lg'),\r\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\r\n 'cide-element-input-label-start': (labelDir === 'start'),\r\n 'cide-element-input-label-end': (labelDir === 'end'),\r\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\r\n 'cide-element-input-label-less': (!label || labelHide),\r\n 'cide-element-style-outline': (fill === 'outline'),\r\n 'cide-element-style-solid': (fill === 'solid'),\r\n 'cide-element-style-standard': (fill === 'standard'),\r\n}\">\r\n @if (label && !labelHide) {\r\n <label [for]=\"id\" class=\"cide-select-label\">\r\n {{ label }}\r\n @if (required) {\r\n <span class=\"tw-text-red-500 tw-ml-1\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"cide-element-select-wrapper tw-relative\">\r\n <!-- Leading Icon -->\r\n @if (leadingIcon) {\r\n <span class=\"cide-input-leading-icon-wrapper\">\r\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\r\n </span>\r\n }\r\n\r\n <!-- Select Button -->\r\n <button type=\"button\" [id]=\"id\" [disabled]=\"disabled\" (click)=\"toggleDropdown()\" (blur)=\"onBlur()\" [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIcon || clearInput || loading,\r\n 'tw-pr-1': !trailingIcon && !clearInput && !loading,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n 'tw-h-7': size === 'sm',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled,\r\n 'cide-select-button tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-p-0 tw-outline-none tw-text-left tw-cursor-pointer': true\r\n }\" class=\"cide-select-button\">\r\n\r\n <span class=\"cide-select-value\" [ngClass]=\"{'tw-text-gray-400': !ngModel}\">\r\n {{ ngModel ? getSelectedOptionLabel() : placeholder }}\r\n </span>\r\n </button>\r\n\r\n <!-- Trailing Icon (Dropdown Arrow or Loading Spinner) -->\r\n @if (!clearInput || !ngModel || loading) {\r\n <span class=\"tw-absolute tw-top-1/2 tw-right-0 tw-transform -tw-translate-y-1/2 tw-select-none tw-z-10\">\r\n <!-- Loading Spinner -->\r\n @if (loading) {\r\n <span class=\"tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-500\">\r\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\r\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\r\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\r\n </path>\r\n </svg>\r\n </span>\r\n }\r\n <!-- Dropdown Arrow -->\r\n @if (!loading) {\r\n <span\r\n class=\"material-symbols-outlined tw-w-8 tw-h-8 tw-flex tw-items-center tw-justify-center !tw-text-2xl tw-transition-transform tw-text-gray-500\"\r\n [ngClass]=\"{'tw-rotate-180': isOpen}\">\r\n expand_more\r\n </span>\r\n }\r\n </span>\r\n }\r\n\r\n <!-- Clear Button -->\r\n @if (clearInput && ngModel) {\r\n <button class=\"cide-input-clear\" (click)=\"clearSelection()\" type=\"button\">\r\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\r\n </button>\r\n }\r\n\r\n\r\n </div>\r\n\r\n <!-- Error/Helper Text -->\r\n @if ((errorText || helperText) && !isValid) {\r\n <span class=\"cide-select-help-error-text\">\r\n {{ errorText || helperText }}\r\n </span>\r\n }\r\n @if (helperText && isValid) {\r\n <span class=\"cide-select-help-error-text\">\r\n {{ helperText }}\r\n </span>\r\n }\r\n\r\n <!-- Portal Container -->\r\n <div #dropdownContainer></div>\r\n</div>\r\n\r\n<!-- Portal Template for Dropdown -->\r\n<ng-template #dropdownTemplate let-context=\"context\">\r\n <div\r\n class=\"cide-select-dropdown-portal tw-bg-white tw-border tw-border-gray-300 tw-rounded-md tw-shadow-lg tw-max-h-60 tw-overflow-y-auto tw-z-50\">\r\n <!-- Search Input (if searchable and showSearchInput is true) -->\r\n @if (context?.searchable && showSearchInput) {\r\n <div class=\"tw-p-2 tw-border-b tw-border-gray-200\">\r\n <input #searchInput type=\"text\" placeholder=\"Search options...\" [value]=\"context?.searchTerm\"\r\n (input)=\"context?.onSearchInput($event)\" (mousedown)=\"onDropdownMouseDown()\" (focus)=\"onDropdownMouseDown()\"\r\n (click)=\"$event.stopPropagation()\" (blur)=\"onSearchInputBlur($event)\"\r\n class=\"tw-w-full tw-px-2 tw-py-1 tw-text-sm tw-border tw-border-gray-300 tw-rounded tw-outline-none focus:tw-border-blue-500\">\r\n </div>\r\n }\r\n\r\n <!-- Options List -->\r\n <div class=\"tw-py-1\">\r\n <!-- Loading State -->\r\n @if (context?.loading) {\r\n <div class=\"tw-px-3 tw-py-4 tw-text-center\">\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-space-x-2\">\r\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4 tw-text-gray-500\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\r\n <path class=\"tw-opacity-75\" fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\">\r\n </path>\r\n </svg>\r\n <span class=\"tw-text-sm tw-text-gray-500\">Loading...</span>\r\n </div>\r\n </div>\r\n }\r\n <!-- Options -->\r\n @if (!context?.loading) {\r\n @for (option of context?.options; track $index) {\r\n <button type=\"button\" (mousedown)=\"onDropdownMouseDown()\" (click)=\"context?.selectOption(option)\"\r\n (keyup.enter)=\"context?.selectOption(option)\" (keyup.space)=\"context?.selectOption(option)\"\r\n [disabled]=\"option.disabled\" [ngClass]=\"{\r\n 'cide-select-option tw-w-full tw-text-left tw-px-3 tw-py-2 tw-text-sm tw-cursor-pointer tw-transition-colors hover:tw-bg-gray-100 tw-border-none tw-bg-transparent tw-outline-none': true,\r\n 'tw-bg-blue-50 tw-text-blue-700': context?.isOptionSelected(option),\r\n 'tw-text-gray-400 tw-cursor-not-allowed': option.disabled\r\n }\" class=\"cide-select-option\">\r\n {{ context?.getOptionLabel(option) }}\r\n </button>\r\n }\r\n }\r\n\r\n <!-- No options message -->\r\n @if (!context?.loading && context?.options.length === 0) {\r\n <div class=\"tw-px-3 tw-py-2 tw-text-sm tw-text-gray-500 tw-italic\">\r\n {{ context?.searchable && context?.searchTerm ? 'No options found' : 'No options available' }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</ng-template>", styles: [".cide-select{position:relative;width:100%}.cide-select .cide-select-button{display:flex;align-items:center;justify-content:space-between;width:100%;background-color:transparent;transition:all .2s ease-in-out;cursor:pointer;outline:none}.cide-select .cide-select-button:disabled{cursor:not-allowed}.cide-select .cide-select-value{flex:1;text-align:left}.cide-select .cide-select-dropdown{position:absolute;left:0;right:0;z-index:1000;background-color:#fff;border:1px solid var(--cide-input-border);border-radius:.375rem;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;max-height:15rem;overflow-y:auto}.cide-select .cide-select-dropdown .cide-select-option{display:block;width:100%;text-align:left;padding:.5rem .75rem;font-size:.875rem;cursor:pointer;transition:background-color .15s ease-in-out;border:none;background:transparent;outline:none}.cide-select .cide-select-dropdown .cide-select-option:hover:not(:disabled){background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:focus{background-color:#f3f4f6}.cide-select .cide-select-dropdown .cide-select-option:disabled{cursor:not-allowed}.cide-select-dropdown-portal{width:auto!important;min-width:200px;max-width:80vw;word-wrap:break-word;overflow-wrap:break-word}.cide-select-dropdown-portal .tw-py-1{min-width:0}.cide-select-dropdown-portal .cide-select-option{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"] }]
|
|
2155
2593
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { optionComponents: [{
|
|
2156
2594
|
type: ContentChildren,
|
|
2157
2595
|
args: [CideSelectOptionComponent]
|
|
@@ -4678,7 +5116,7 @@ class CideEleFileInputComponent {
|
|
|
4678
5116
|
useExisting: CideEleFileInputComponent,
|
|
4679
5117
|
multi: true
|
|
4680
5118
|
}
|
|
4681
|
-
], usesOnChanges: true, ngImport: i0, template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"tw-relative\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getPreviewBoxClasses()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\n <div class=\"tw-mb-2\">\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"tw-relative tw-w-full tw-h-full\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\n <!-- Icon and Text -->\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" \n [class]=\"getIconClasses()\" \n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\n @if (isDragOver()) {\n <span class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"tw-mt-3\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5119
|
+
], usesOnChanges: true, ngImport: i0, template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\r\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\r\n @if (labelSignal() && !isPreviewBoxMode()) {\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\r\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\r\n </label>\r\n }\r\n \r\n <!-- Preview Box Mode -->\r\n @if (isPreviewBoxMode()) {\r\n <div class=\"tw-relative\">\r\n <!-- Hidden file input -->\r\n <input\r\n type=\"file\"\r\n [attr.id]=\"'cide-file-input-' + id()\"\r\n [attr.accept]=\"acceptSignal()\"\r\n [attr.multiple]=\"multipleSignal() ? true : null\"\r\n [disabled]=\"disabledSignal()\"\r\n (change)=\"onFileSelected($event)\"\r\n class=\"tw-hidden\"\r\n />\r\n \r\n <!-- Preview Box -->\r\n <div \r\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\r\n [class]=\"getPreviewBoxClasses()\"\r\n [style.width]=\"previewWidthSignal()\"\r\n [style.height]=\"previewHeightSignal()\"\r\n (click)=\"triggerFileSelect()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragenter)=\"onDragEnter($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\r\n \r\n <!-- No Image State -->\r\n @if (!hasImages()) {\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\r\n <div class=\"tw-mb-2\">\r\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\r\n </div>\r\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\r\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\r\n </div>\r\n </div>\r\n }\r\n \r\n <!-- Image Preview State -->\r\n @if (hasImages()) {\r\n <div class=\"tw-relative tw-w-full tw-h-full\">\r\n <img \r\n [src]=\"previewUrls()[0]\" \r\n [alt]=\"fileNames()[0] || 'Preview image'\"\r\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\r\n <div class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\r\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">Click to change</div>\r\n </div>\r\n @if (!disabledSignal()) {\r\n <button \r\n type=\"button\" \r\n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\r\n (click)=\"clearFiles(); $event.stopPropagation()\"\r\n title=\"Remove image\">\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n \r\n <!-- File name display for preview box mode -->\r\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\r\n <div class=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\r\n {{ fileNames()[0] }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Standard Mode -->\r\n @if (!isPreviewBoxMode()) {\r\n <!-- Hidden file input -->\r\n <input\r\n type=\"file\"\r\n [attr.id]=\"'cide-file-input-' + id()\"\r\n [attr.accept]=\"acceptSignal()\"\r\n [attr.multiple]=\"multipleSignal() ? true : null\"\r\n [disabled]=\"disabledSignal()\"\r\n (change)=\"onFileSelected($event)\"\r\n class=\"tw-hidden\"\r\n />\r\n \r\n <!-- Modern Drag and Drop Zone -->\r\n <div \r\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\r\n [class]=\"getDragDropZoneClasses()\"\r\n (click)=\"triggerFileSelect()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragenter)=\"onDragEnter($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\">\r\n \r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\r\n <!-- Icon and Text -->\r\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\r\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" \r\n [class]=\"getIconClasses()\" \r\n size=\"sm\">\r\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\r\n </cide-ele-icon>\r\n \r\n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\r\n @if (isDragOver()) {\r\n <span class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop files here</span>\r\n } @else if (hasFiles()) {\r\n <span class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\r\n @if (multipleSignal() && fileNames().length > 1) {\r\n {{ fileNames().length }} files selected\r\n } @else {\r\n {{ fileNames()[0] }}\r\n }\r\n </span>\r\n @if (totalFileSize() > 0) {\r\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\r\n }\r\n } @else {\r\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\r\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n \r\n <!-- Action Buttons -->\r\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\r\n @if (hasFiles()) {\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\" \r\n (click)=\"clearFiles(); $event.stopPropagation()\"\r\n title=\"Clear files\">\r\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n \r\n <!-- Image Preview Section (only for standard mode) -->\r\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\r\n <div class=\"tw-mt-3\">\r\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\r\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\r\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\r\n <div \r\n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\r\n [style.width]=\"previewWidthSignal()\"\r\n [style.height]=\"previewHeightSignal()\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\r\n (click)=\"removePreview(i)\"\r\n title=\"Remove image\">\r\n \u00D7\r\n </button>\r\n <img \r\n [src]=\"previewUrl\" \r\n [alt]=\"fileNames()[i] || 'Preview image'\"\r\n class=\"tw-w-full tw-h-full tw-object-cover\"\r\n loading=\"lazy\">\r\n <div class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">{{ fileNames()[i] }}</div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n \r\n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\r\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\r\n <div class=\"tw-flex tw-items-center tw-gap-2\">\r\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\r\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\r\n @if (hasActiveUploads()) {\r\n {{ getActiveUploadCount() }} uploading\r\n } @else if (getUploadCount() > 0) {\r\n {{ getUploadCount() }} completed\r\n } @else if (hasEverUploaded()) {\r\n View uploads\r\n }\r\n </span>\r\n </div>\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\r\n (click)=\"showFloatingUploaderDialog()\"\r\n title=\"View upload progress and history\">\r\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\r\n </button>\r\n </div>\r\n }\r\n \r\n @if (errorTextSignal()) {\r\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\r\n }\r\n @if (helperTextSignal() && !errorTextSignal()) {\r\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\r\n }\r\n</div> ", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
4682
5120
|
}
|
|
4683
5121
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
4684
5122
|
type: Component,
|
|
@@ -4693,7 +5131,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
4693
5131
|
useExisting: CideEleFileInputComponent,
|
|
4694
5132
|
multi: true
|
|
4695
5133
|
}
|
|
4696
|
-
], template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"tw-relative\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getPreviewBoxClasses()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\n <div class=\"tw-mb-2\">\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"tw-relative tw-w-full tw-h-full\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\n <!-- Icon and Text -->\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" \n [class]=\"getIconClasses()\" \n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\n @if (isDragOver()) {\n <span class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"tw-mt-3\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> " }]
|
|
5134
|
+
], template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\r\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\r\n @if (labelSignal() && !isPreviewBoxMode()) {\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\r\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\r\n </label>\r\n }\r\n \r\n <!-- Preview Box Mode -->\r\n @if (isPreviewBoxMode()) {\r\n <div class=\"tw-relative\">\r\n <!-- Hidden file input -->\r\n <input\r\n type=\"file\"\r\n [attr.id]=\"'cide-file-input-' + id()\"\r\n [attr.accept]=\"acceptSignal()\"\r\n [attr.multiple]=\"multipleSignal() ? true : null\"\r\n [disabled]=\"disabledSignal()\"\r\n (change)=\"onFileSelected($event)\"\r\n class=\"tw-hidden\"\r\n />\r\n \r\n <!-- Preview Box -->\r\n <div \r\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\r\n [class]=\"getPreviewBoxClasses()\"\r\n [style.width]=\"previewWidthSignal()\"\r\n [style.height]=\"previewHeightSignal()\"\r\n (click)=\"triggerFileSelect()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragenter)=\"onDragEnter($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\r\n \r\n <!-- No Image State -->\r\n @if (!hasImages()) {\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\r\n <div class=\"tw-mb-2\">\r\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\r\n </div>\r\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\r\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\r\n </div>\r\n </div>\r\n }\r\n \r\n <!-- Image Preview State -->\r\n @if (hasImages()) {\r\n <div class=\"tw-relative tw-w-full tw-h-full\">\r\n <img \r\n [src]=\"previewUrls()[0]\" \r\n [alt]=\"fileNames()[0] || 'Preview image'\"\r\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\r\n <div class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\r\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">Click to change</div>\r\n </div>\r\n @if (!disabledSignal()) {\r\n <button \r\n type=\"button\" \r\n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\r\n (click)=\"clearFiles(); $event.stopPropagation()\"\r\n title=\"Remove image\">\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n \r\n <!-- File name display for preview box mode -->\r\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\r\n <div class=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\r\n {{ fileNames()[0] }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Standard Mode -->\r\n @if (!isPreviewBoxMode()) {\r\n <!-- Hidden file input -->\r\n <input\r\n type=\"file\"\r\n [attr.id]=\"'cide-file-input-' + id()\"\r\n [attr.accept]=\"acceptSignal()\"\r\n [attr.multiple]=\"multipleSignal() ? true : null\"\r\n [disabled]=\"disabledSignal()\"\r\n (change)=\"onFileSelected($event)\"\r\n class=\"tw-hidden\"\r\n />\r\n \r\n <!-- Modern Drag and Drop Zone -->\r\n <div \r\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\r\n [class]=\"getDragDropZoneClasses()\"\r\n (click)=\"triggerFileSelect()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragenter)=\"onDragEnter($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\">\r\n \r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\r\n <!-- Icon and Text -->\r\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\r\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" \r\n [class]=\"getIconClasses()\" \r\n size=\"sm\">\r\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\r\n </cide-ele-icon>\r\n \r\n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\r\n @if (isDragOver()) {\r\n <span class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop files here</span>\r\n } @else if (hasFiles()) {\r\n <span class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\r\n @if (multipleSignal() && fileNames().length > 1) {\r\n {{ fileNames().length }} files selected\r\n } @else {\r\n {{ fileNames()[0] }}\r\n }\r\n </span>\r\n @if (totalFileSize() > 0) {\r\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\r\n }\r\n } @else {\r\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\r\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n \r\n <!-- Action Buttons -->\r\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\r\n @if (hasFiles()) {\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\" \r\n (click)=\"clearFiles(); $event.stopPropagation()\"\r\n title=\"Clear files\">\r\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n \r\n <!-- Image Preview Section (only for standard mode) -->\r\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\r\n <div class=\"tw-mt-3\">\r\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\r\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\r\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\r\n <div \r\n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\r\n [style.width]=\"previewWidthSignal()\"\r\n [style.height]=\"previewHeightSignal()\">\r\n <button \r\n type=\"button\" \r\n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\r\n (click)=\"removePreview(i)\"\r\n title=\"Remove image\">\r\n \u00D7\r\n </button>\r\n <img \r\n [src]=\"previewUrl\" \r\n [alt]=\"fileNames()[i] || 'Preview image'\"\r\n class=\"tw-w-full tw-h-full tw-object-cover\"\r\n loading=\"lazy\">\r\n <div class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">{{ fileNames()[i] }}</div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n \r\n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\r\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\r\n <div class=\"tw-flex tw-items-center tw-gap-2\">\r\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\r\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\r\n @if (hasActiveUploads()) {\r\n {{ getActiveUploadCount() }} uploading\r\n } @else if (getUploadCount() > 0) {\r\n {{ getUploadCount() }} completed\r\n } @else if (hasEverUploaded()) {\r\n View uploads\r\n }\r\n </span>\r\n </div>\r\n <button \r\n type=\"button\" \r\n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\r\n (click)=\"showFloatingUploaderDialog()\"\r\n title=\"View upload progress and history\">\r\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\r\n </button>\r\n </div>\r\n }\r\n \r\n @if (errorTextSignal()) {\r\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\r\n }\r\n @if (helperTextSignal() && !errorTextSignal()) {\r\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\r\n }\r\n</div> " }]
|
|
4697
5135
|
}], ctorParameters: () => [], propDecorators: { label: [{
|
|
4698
5136
|
type: Input
|
|
4699
5137
|
}], accept: [{
|
|
@@ -5173,14 +5611,14 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
5173
5611
|
});
|
|
5174
5612
|
}
|
|
5175
5613
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5176
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingFileUploaderComponent, isStandalone: true, selector: "cide-ele-floating-file-uploader", inputs: { data: "data" }, viewQueries: [{ propertyName: "fileInputRef", first: true, predicate: ["fileInput"], descendants: true }], ngImport: i0, template: "<!-- File Uploader Content (No absolute positioning - works within floating container) -->\n@if (isVisible()) {\n<div class=\"tw-w-full tw-h-full tw-flex tw-flex-col tw-overflow-hidden\">\n <!-- Content starts directly - no header needed since floating container provides it -->\n\n <!-- Content -->\n <div class=\"tw-flex-1 tw-overflow-y-auto tw-p-4 tw-pb-4\">\n \n <!-- Drag and Drop Zone -->\n <div class=\"tw-mb-4 tw-p-6 tw-border-2 tw-border-dashed tw-border-gray-300 tw-rounded-lg tw-bg-gray-50 tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-center hover:tw-border-blue-500 hover:tw-bg-blue-50\" \n [class.tw-border-blue-500]=\"isDragOver()\"\n [class.tw-bg-blue-100]=\"isDragOver()\"\n [class.tw-scale-105]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\" \n (drop)=\"onDrop($event)\" \n (click)=\"triggerFileInput()\">\n \n <!-- Hidden file input -->\n <input #fileInput \n type=\"file\" \n [multiple]=\"data.multiple !== false\" \n [accept]=\"data.allowedFileTypes?.join(',') || '*/*'\" \n (change)=\"onFileInputChange($event)\" \n class=\"tw-hidden\">\n \n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-gray-400 tw-transition-colors tw-duration-200\" \n [class.tw-text-blue-500]=\"isDragOver()\"\n size=\"sm\">cloud_upload</cide-ele-icon>\n \n <div>\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700\">\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\n </div>\n @if (data.description) {\n <div class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ data.description }}</div>\n }\n </div>\n </div>\n </div>\n \n <!-- Upload Queue - Show files from service state -->\n @if (allFilesForGroup().length > 0) {\n <div class=\"tw-space-y-2\">\n <!-- Show all files from service state -->\n @for (file of allFilesForGroup(); track file.fileId) {\n <div class=\"tw-flex tw-items-center tw-px-4 tw-py-3 tw-rounded-lg tw-transition-colors tw-duration-200 tw-bg-white tw-border tw-border-gray-200 hover:tw-bg-gray-100 hover:tw-shadow-sm\"\n [class.tw-bg-blue-50]=\"file.stage === 'uploading'\"\n [class.tw-bg-green-50]=\"file.stage === 'complete'\"\n [class.tw-bg-red-50]=\"file.stage === 'error'\">\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\n <div class=\"tw-min-w-0 tw-flex-1\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-900 tw-truncate\">{{ file.fileName }}</div>\n <div class=\"tw-text-xs\">\n @switch (file.stage) {\n @case ('pending') {\n <span class=\"tw-text-yellow-600 tw-font-medium\">Waiting...</span>\n }\n @case ('reading') {\n <span class=\"tw-text-yellow-600 tw-font-medium\">Reading...</span>\n }\n @case ('uploading') {\n <span class=\"tw-text-blue-600 tw-font-medium\">Uploading...</span>\n }\n @case ('complete') {\n <span class=\"tw-text-green-600 tw-font-medium\">Completed</span>\n }\n @case ('error') {\n <span class=\"tw-text-red-600 tw-font-medium\">Failed</span>\n }\n }\n </div>\n </div>\n </div>\n\n <!-- Progress Bar (only for uploading files) -->\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-ml-2 tw-min-w-[80px]\">\n <div class=\"tw-flex-1 tw-h-1 tw-bg-gray-200 tw-rounded-full tw-overflow-hidden\">\n <div class=\"tw-h-full tw-bg-blue-500 tw-transition-all tw-duration-300\" [style.width.%]=\"file.percentage\"></div>\n </div>\n <span class=\"tw-text-xs tw-text-gray-500 tw-min-w-[24px] tw-text-right\">{{ file.percentage }}%</span>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"tw-flex tw-gap-1 tw-ml-2\">\n @switch (file.stage) {\n @case ('pending') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('reading') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('uploading') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('complete') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-text-green-600\" title=\"Completed\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n </button>\n }\n @case ('error') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-blue-50 hover:tw-text-blue-600\" \n title=\"Retry\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- No uploads message when manually opened -->\n <div class=\"tw-py-8 tw-text-center tw-text-gray-500\">\n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-4\">\n <cide-ele-icon size=\"md\" class=\"tw-text-gray-300 tw-opacity-70\">cloud_upload</cide-ele-icon>\n <div>\n <h4 class=\"tw-text-lg tw-font-semibold tw-text-gray-700 tw-mb-2\">No active uploads</h4>\n <p class=\"tw-text-sm tw-text-gray-500\">Upload files to see their progress here</p>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Status summary - integrated into content -->\n @if (hasActiveUploads()) {\n <div class=\"tw-px-4 tw-py-2 tw-bg-gray-50\">\n <div class=\"tw-flex tw-gap-3 tw-text-xs\">\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-blue-600\">\n <cide-ele-icon size=\"xs\">upload</cide-ele-icon>\n <span>{{ getUploadingCount() }} uploading</span>\n </div>\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-green-600\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n <span>{{ getCompletedCount() }} completed</span>\n </div>\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-red-600\">\n <cide-ele-icon size=\"xs\">error</cide-ele-icon>\n <span>{{ getFailedCount() }} failed</span>\n </div>\n </div>\n </div>\n }\n</div>\n}", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5614
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingFileUploaderComponent, isStandalone: true, selector: "cide-ele-floating-file-uploader", inputs: { data: "data" }, viewQueries: [{ propertyName: "fileInputRef", first: true, predicate: ["fileInput"], descendants: true }], ngImport: i0, template: "<!-- File Uploader Content (No absolute positioning - works within floating container) -->\r\n@if (isVisible()) {\r\n<div class=\"tw-w-full tw-h-full tw-flex tw-flex-col tw-overflow-hidden\">\r\n <!-- Content starts directly - no header needed since floating container provides it -->\r\n\r\n <!-- Content -->\r\n <div class=\"tw-flex-1 tw-overflow-y-auto tw-p-4 tw-pb-4\">\r\n \r\n <!-- Drag and Drop Zone -->\r\n <div class=\"tw-mb-4 tw-p-6 tw-border-2 tw-border-dashed tw-border-gray-300 tw-rounded-lg tw-bg-gray-50 tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-center hover:tw-border-blue-500 hover:tw-bg-blue-50\" \r\n [class.tw-border-blue-500]=\"isDragOver()\"\r\n [class.tw-bg-blue-100]=\"isDragOver()\"\r\n [class.tw-scale-105]=\"isDragOver()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\" \r\n (drop)=\"onDrop($event)\" \r\n (click)=\"triggerFileInput()\">\r\n \r\n <!-- Hidden file input -->\r\n <input #fileInput \r\n type=\"file\" \r\n [multiple]=\"data.multiple !== false\" \r\n [accept]=\"data.allowedFileTypes?.join(',') || '*/*'\" \r\n (change)=\"onFileInputChange($event)\" \r\n class=\"tw-hidden\">\r\n \r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-2\">\r\n <cide-ele-icon class=\"tw-text-gray-400 tw-transition-colors tw-duration-200\" \r\n [class.tw-text-blue-500]=\"isDragOver()\"\r\n size=\"sm\">cloud_upload</cide-ele-icon>\r\n \r\n <div>\r\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700\">\r\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\r\n </div>\r\n @if (data.description) {\r\n <div class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ data.description }}</div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- Upload Queue - Show files from service state -->\r\n @if (allFilesForGroup().length > 0) {\r\n <div class=\"tw-space-y-2\">\r\n <!-- Show all files from service state -->\r\n @for (file of allFilesForGroup(); track file.fileId) {\r\n <div class=\"tw-flex tw-items-center tw-px-4 tw-py-3 tw-rounded-lg tw-transition-colors tw-duration-200 tw-bg-white tw-border tw-border-gray-200 hover:tw-bg-gray-100 hover:tw-shadow-sm\"\r\n [class.tw-bg-blue-50]=\"file.stage === 'uploading'\"\r\n [class.tw-bg-green-50]=\"file.stage === 'complete'\"\r\n [class.tw-bg-red-50]=\"file.stage === 'error'\">\r\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-flex-1 tw-min-w-0\">\r\n <cide-ele-icon class=\"tw-flex-shrink-0\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\r\n <div class=\"tw-min-w-0 tw-flex-1\">\r\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-900 tw-truncate\">{{ file.fileName }}</div>\r\n <div class=\"tw-text-xs\">\r\n @switch (file.stage) {\r\n @case ('pending') {\r\n <span class=\"tw-text-yellow-600 tw-font-medium\">Waiting...</span>\r\n }\r\n @case ('reading') {\r\n <span class=\"tw-text-yellow-600 tw-font-medium\">Reading...</span>\r\n }\r\n @case ('uploading') {\r\n <span class=\"tw-text-blue-600 tw-font-medium\">Uploading...</span>\r\n }\r\n @case ('complete') {\r\n <span class=\"tw-text-green-600 tw-font-medium\">Completed</span>\r\n }\r\n @case ('error') {\r\n <span class=\"tw-text-red-600 tw-font-medium\">Failed</span>\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Progress Bar (only for uploading files) -->\r\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\r\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-ml-2 tw-min-w-[80px]\">\r\n <div class=\"tw-flex-1 tw-h-1 tw-bg-gray-200 tw-rounded-full tw-overflow-hidden\">\r\n <div class=\"tw-h-full tw-bg-blue-500 tw-transition-all tw-duration-300\" [style.width.%]=\"file.percentage\"></div>\r\n </div>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-min-w-[24px] tw-text-right\">{{ file.percentage }}%</span>\r\n </div>\r\n }\r\n\r\n <!-- Actions -->\r\n <div class=\"tw-flex tw-gap-1 tw-ml-2\">\r\n @switch (file.stage) {\r\n @case ('pending') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \r\n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\r\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('reading') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \r\n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\r\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('uploading') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \r\n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\r\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('complete') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-text-green-600\" title=\"Completed\">\r\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('error') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-blue-50 hover:tw-text-blue-600\" \r\n title=\"Retry\">\r\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <!-- No uploads message when manually opened -->\r\n <div class=\"tw-py-8 tw-text-center tw-text-gray-500\">\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-4\">\r\n <cide-ele-icon size=\"md\" class=\"tw-text-gray-300 tw-opacity-70\">cloud_upload</cide-ele-icon>\r\n <div>\r\n <h4 class=\"tw-text-lg tw-font-semibold tw-text-gray-700 tw-mb-2\">No active uploads</h4>\r\n <p class=\"tw-text-sm tw-text-gray-500\">Upload files to see their progress here</p>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Status summary - integrated into content -->\r\n @if (hasActiveUploads()) {\r\n <div class=\"tw-px-4 tw-py-2 tw-bg-gray-50\">\r\n <div class=\"tw-flex tw-gap-3 tw-text-xs\">\r\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-blue-600\">\r\n <cide-ele-icon size=\"xs\">upload</cide-ele-icon>\r\n <span>{{ getUploadingCount() }} uploading</span>\r\n </div>\r\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-green-600\">\r\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\r\n <span>{{ getCompletedCount() }} completed</span>\r\n </div>\r\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-red-600\">\r\n <cide-ele-icon size=\"xs\">error</cide-ele-icon>\r\n <span>{{ getFailedCount() }} failed</span>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n}", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5177
5615
|
}
|
|
5178
5616
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, decorators: [{
|
|
5179
5617
|
type: Component,
|
|
5180
5618
|
args: [{ selector: 'cide-ele-floating-file-uploader', standalone: true, imports: [
|
|
5181
5619
|
CommonModule,
|
|
5182
5620
|
CideIconComponent
|
|
5183
|
-
], template: "<!-- File Uploader Content (No absolute positioning - works within floating container) -->\n@if (isVisible()) {\n<div class=\"tw-w-full tw-h-full tw-flex tw-flex-col tw-overflow-hidden\">\n <!-- Content starts directly - no header needed since floating container provides it -->\n\n <!-- Content -->\n <div class=\"tw-flex-1 tw-overflow-y-auto tw-p-4 tw-pb-4\">\n \n <!-- Drag and Drop Zone -->\n <div class=\"tw-mb-4 tw-p-6 tw-border-2 tw-border-dashed tw-border-gray-300 tw-rounded-lg tw-bg-gray-50 tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-center hover:tw-border-blue-500 hover:tw-bg-blue-50\" \n [class.tw-border-blue-500]=\"isDragOver()\"\n [class.tw-bg-blue-100]=\"isDragOver()\"\n [class.tw-scale-105]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\" \n (drop)=\"onDrop($event)\" \n (click)=\"triggerFileInput()\">\n \n <!-- Hidden file input -->\n <input #fileInput \n type=\"file\" \n [multiple]=\"data.multiple !== false\" \n [accept]=\"data.allowedFileTypes?.join(',') || '*/*'\" \n (change)=\"onFileInputChange($event)\" \n class=\"tw-hidden\">\n \n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-gray-400 tw-transition-colors tw-duration-200\" \n [class.tw-text-blue-500]=\"isDragOver()\"\n size=\"sm\">cloud_upload</cide-ele-icon>\n \n <div>\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700\">\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\n </div>\n @if (data.description) {\n <div class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ data.description }}</div>\n }\n </div>\n </div>\n </div>\n \n <!-- Upload Queue - Show files from service state -->\n @if (allFilesForGroup().length > 0) {\n <div class=\"tw-space-y-2\">\n <!-- Show all files from service state -->\n @for (file of allFilesForGroup(); track file.fileId) {\n <div class=\"tw-flex tw-items-center tw-px-4 tw-py-3 tw-rounded-lg tw-transition-colors tw-duration-200 tw-bg-white tw-border tw-border-gray-200 hover:tw-bg-gray-100 hover:tw-shadow-sm\"\n [class.tw-bg-blue-50]=\"file.stage === 'uploading'\"\n [class.tw-bg-green-50]=\"file.stage === 'complete'\"\n [class.tw-bg-red-50]=\"file.stage === 'error'\">\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\n <div class=\"tw-min-w-0 tw-flex-1\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-900 tw-truncate\">{{ file.fileName }}</div>\n <div class=\"tw-text-xs\">\n @switch (file.stage) {\n @case ('pending') {\n <span class=\"tw-text-yellow-600 tw-font-medium\">Waiting...</span>\n }\n @case ('reading') {\n <span class=\"tw-text-yellow-600 tw-font-medium\">Reading...</span>\n }\n @case ('uploading') {\n <span class=\"tw-text-blue-600 tw-font-medium\">Uploading...</span>\n }\n @case ('complete') {\n <span class=\"tw-text-green-600 tw-font-medium\">Completed</span>\n }\n @case ('error') {\n <span class=\"tw-text-red-600 tw-font-medium\">Failed</span>\n }\n }\n </div>\n </div>\n </div>\n\n <!-- Progress Bar (only for uploading files) -->\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-ml-2 tw-min-w-[80px]\">\n <div class=\"tw-flex-1 tw-h-1 tw-bg-gray-200 tw-rounded-full tw-overflow-hidden\">\n <div class=\"tw-h-full tw-bg-blue-500 tw-transition-all tw-duration-300\" [style.width.%]=\"file.percentage\"></div>\n </div>\n <span class=\"tw-text-xs tw-text-gray-500 tw-min-w-[24px] tw-text-right\">{{ file.percentage }}%</span>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"tw-flex tw-gap-1 tw-ml-2\">\n @switch (file.stage) {\n @case ('pending') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('reading') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('uploading') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('complete') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-text-green-600\" title=\"Completed\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n </button>\n }\n @case ('error') {\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-blue-50 hover:tw-text-blue-600\" \n title=\"Retry\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- No uploads message when manually opened -->\n <div class=\"tw-py-8 tw-text-center tw-text-gray-500\">\n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-4\">\n <cide-ele-icon size=\"md\" class=\"tw-text-gray-300 tw-opacity-70\">cloud_upload</cide-ele-icon>\n <div>\n <h4 class=\"tw-text-lg tw-font-semibold tw-text-gray-700 tw-mb-2\">No active uploads</h4>\n <p class=\"tw-text-sm tw-text-gray-500\">Upload files to see their progress here</p>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Status summary - integrated into content -->\n @if (hasActiveUploads()) {\n <div class=\"tw-px-4 tw-py-2 tw-bg-gray-50\">\n <div class=\"tw-flex tw-gap-3 tw-text-xs\">\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-blue-600\">\n <cide-ele-icon size=\"xs\">upload</cide-ele-icon>\n <span>{{ getUploadingCount() }} uploading</span>\n </div>\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-green-600\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n <span>{{ getCompletedCount() }} completed</span>\n </div>\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-red-600\">\n <cide-ele-icon size=\"xs\">error</cide-ele-icon>\n <span>{{ getFailedCount() }} failed</span>\n </div>\n </div>\n </div>\n }\n</div>\n}" }]
|
|
5621
|
+
], template: "<!-- File Uploader Content (No absolute positioning - works within floating container) -->\r\n@if (isVisible()) {\r\n<div class=\"tw-w-full tw-h-full tw-flex tw-flex-col tw-overflow-hidden\">\r\n <!-- Content starts directly - no header needed since floating container provides it -->\r\n\r\n <!-- Content -->\r\n <div class=\"tw-flex-1 tw-overflow-y-auto tw-p-4 tw-pb-4\">\r\n \r\n <!-- Drag and Drop Zone -->\r\n <div class=\"tw-mb-4 tw-p-6 tw-border-2 tw-border-dashed tw-border-gray-300 tw-rounded-lg tw-bg-gray-50 tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-center hover:tw-border-blue-500 hover:tw-bg-blue-50\" \r\n [class.tw-border-blue-500]=\"isDragOver()\"\r\n [class.tw-bg-blue-100]=\"isDragOver()\"\r\n [class.tw-scale-105]=\"isDragOver()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\" \r\n (drop)=\"onDrop($event)\" \r\n (click)=\"triggerFileInput()\">\r\n \r\n <!-- Hidden file input -->\r\n <input #fileInput \r\n type=\"file\" \r\n [multiple]=\"data.multiple !== false\" \r\n [accept]=\"data.allowedFileTypes?.join(',') || '*/*'\" \r\n (change)=\"onFileInputChange($event)\" \r\n class=\"tw-hidden\">\r\n \r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-2\">\r\n <cide-ele-icon class=\"tw-text-gray-400 tw-transition-colors tw-duration-200\" \r\n [class.tw-text-blue-500]=\"isDragOver()\"\r\n size=\"sm\">cloud_upload</cide-ele-icon>\r\n \r\n <div>\r\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700\">\r\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\r\n </div>\r\n @if (data.description) {\r\n <div class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ data.description }}</div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- Upload Queue - Show files from service state -->\r\n @if (allFilesForGroup().length > 0) {\r\n <div class=\"tw-space-y-2\">\r\n <!-- Show all files from service state -->\r\n @for (file of allFilesForGroup(); track file.fileId) {\r\n <div class=\"tw-flex tw-items-center tw-px-4 tw-py-3 tw-rounded-lg tw-transition-colors tw-duration-200 tw-bg-white tw-border tw-border-gray-200 hover:tw-bg-gray-100 hover:tw-shadow-sm\"\r\n [class.tw-bg-blue-50]=\"file.stage === 'uploading'\"\r\n [class.tw-bg-green-50]=\"file.stage === 'complete'\"\r\n [class.tw-bg-red-50]=\"file.stage === 'error'\">\r\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-flex-1 tw-min-w-0\">\r\n <cide-ele-icon class=\"tw-flex-shrink-0\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\r\n <div class=\"tw-min-w-0 tw-flex-1\">\r\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-900 tw-truncate\">{{ file.fileName }}</div>\r\n <div class=\"tw-text-xs\">\r\n @switch (file.stage) {\r\n @case ('pending') {\r\n <span class=\"tw-text-yellow-600 tw-font-medium\">Waiting...</span>\r\n }\r\n @case ('reading') {\r\n <span class=\"tw-text-yellow-600 tw-font-medium\">Reading...</span>\r\n }\r\n @case ('uploading') {\r\n <span class=\"tw-text-blue-600 tw-font-medium\">Uploading...</span>\r\n }\r\n @case ('complete') {\r\n <span class=\"tw-text-green-600 tw-font-medium\">Completed</span>\r\n }\r\n @case ('error') {\r\n <span class=\"tw-text-red-600 tw-font-medium\">Failed</span>\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Progress Bar (only for uploading files) -->\r\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\r\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-ml-2 tw-min-w-[80px]\">\r\n <div class=\"tw-flex-1 tw-h-1 tw-bg-gray-200 tw-rounded-full tw-overflow-hidden\">\r\n <div class=\"tw-h-full tw-bg-blue-500 tw-transition-all tw-duration-300\" [style.width.%]=\"file.percentage\"></div>\r\n </div>\r\n <span class=\"tw-text-xs tw-text-gray-500 tw-min-w-[24px] tw-text-right\">{{ file.percentage }}%</span>\r\n </div>\r\n }\r\n\r\n <!-- Actions -->\r\n <div class=\"tw-flex tw-gap-1 tw-ml-2\">\r\n @switch (file.stage) {\r\n @case ('pending') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \r\n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\r\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('reading') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \r\n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\r\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('uploading') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-red-50 hover:tw-text-red-600\" \r\n (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\r\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('complete') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-text-green-600\" title=\"Completed\">\r\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\r\n </button>\r\n }\r\n @case ('error') {\r\n <button class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-border-none tw-bg-transparent tw-rounded tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-gray-400 hover:tw-bg-blue-50 hover:tw-text-blue-600\" \r\n title=\"Retry\">\r\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <!-- No uploads message when manually opened -->\r\n <div class=\"tw-py-8 tw-text-center tw-text-gray-500\">\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-gap-4\">\r\n <cide-ele-icon size=\"md\" class=\"tw-text-gray-300 tw-opacity-70\">cloud_upload</cide-ele-icon>\r\n <div>\r\n <h4 class=\"tw-text-lg tw-font-semibold tw-text-gray-700 tw-mb-2\">No active uploads</h4>\r\n <p class=\"tw-text-sm tw-text-gray-500\">Upload files to see their progress here</p>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Status summary - integrated into content -->\r\n @if (hasActiveUploads()) {\r\n <div class=\"tw-px-4 tw-py-2 tw-bg-gray-50\">\r\n <div class=\"tw-flex tw-gap-3 tw-text-xs\">\r\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-blue-600\">\r\n <cide-ele-icon size=\"xs\">upload</cide-ele-icon>\r\n <span>{{ getUploadingCount() }} uploading</span>\r\n </div>\r\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-green-600\">\r\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\r\n <span>{{ getCompletedCount() }} completed</span>\r\n </div>\r\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-text-red-600\">\r\n <cide-ele-icon size=\"xs\">error</cide-ele-icon>\r\n <span>{{ getFailedCount() }} failed</span>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n}" }]
|
|
5184
5622
|
}], ctorParameters: () => [], propDecorators: { fileInputRef: [{
|
|
5185
5623
|
type: ViewChild,
|
|
5186
5624
|
args: ['fileInput']
|
|
@@ -5284,7 +5722,7 @@ class CideTextareaComponent {
|
|
|
5284
5722
|
multi: true,
|
|
5285
5723
|
useExisting: forwardRef(() => CideTextareaComponent),
|
|
5286
5724
|
}
|
|
5287
|
-
], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-textarea\" [ngClass]=\"{\n 'cide-element-size-xxs': (size === '2xs'),\n 'cide-element-size-xs': (size === 'xs'),\n 'cide-element-size-sm': (size === 'sm'),\n 'cide-element-size-md': (size === 'md'),\n 'cide-element-size-lg': (size === 'lg'),\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\n 'cide-element-input-label-start': (labelDir === 'start'),\n 'cide-element-input-label-end': (labelDir === 'end'),\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\n 'cide-element-input-label-less': (!label || labelHide),\n 'cide-element-style-outline': (fill === 'outline'),\n 'cide-element-style-solid': (fill === 'solid'),\n 'cide-element-style-standard': (fill === 'standard'),\n}\">\n <label *ngIf=\"label && !labelHide\" [for]=\"id\" class=\"cide-textarea-label\">\n {{ label }}\n <span *ngIf=\"required\" class=\"tw-text-red-500 tw-ml-1\">*</span>\n </label>\n <div class=\"cide-element-input-wrapper\">\n <!-- Leading Icon -->\n <span class=\"cide-input-leading-icon-wrapper\" *ngIf=\"leadingIcon\">\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\n </span>\n <!-- Trailing Icon -->\n <span class=\"tw-absolute cide-input-trailing-icon -tw-bottom-1 tw-select-none tw-right-0\"\n *ngIf=\"trailingIconInternal\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\n </span>\n <!-- Clear -->\n <button class=\"cide-input-clear\" *ngIf=\"clearInput && ngModel\" (click)=\"ClearInputValue()\">\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\n </button>\n <!-- Textarea -->\n <textarea [id]=\"id\" [attr.placeholder]=\"placeholder\" [attr.rows]=\"rows\"\n [attr.minlength]=\"minlength > 0 ? minlength : null\" [attr.maxlength]=\"maxlength > 0 ? maxlength : null\"\n [disabled]=\"disabled\" [(ngModel)]=\"ngModel\" (input)=\"onInput($event)\" (blur)=\"onBlur()\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n class=\"cide-textarea-input tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-py-[1px] tw-outline-none\"></textarea>\n </div>\n <span *ngIf=\"(errorText || helperText) && !isValid\" class=\"cide-textarea-help-error-text\">{{ errorText || helperText\n }}</span>\n <span *ngIf=\"helperText && isValid\" class=\"cide-textarea-help-error-text\">{{ helperText }}</span>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
5725
|
+
], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-textarea\" [ngClass]=\"{\r\n 'cide-element-size-xxs': (size === '2xs'),\r\n 'cide-element-size-xs': (size === 'xs'),\r\n 'cide-element-size-sm': (size === 'sm'),\r\n 'cide-element-size-md': (size === 'md'),\r\n 'cide-element-size-lg': (size === 'lg'),\r\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\r\n 'cide-element-input-label-start': (labelDir === 'start'),\r\n 'cide-element-input-label-end': (labelDir === 'end'),\r\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\r\n 'cide-element-input-label-less': (!label || labelHide),\r\n 'cide-element-style-outline': (fill === 'outline'),\r\n 'cide-element-style-solid': (fill === 'solid'),\r\n 'cide-element-style-standard': (fill === 'standard'),\r\n}\">\r\n <label *ngIf=\"label && !labelHide\" [for]=\"id\" class=\"cide-textarea-label\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"tw-text-red-500 tw-ml-1\">*</span>\r\n </label>\r\n <div class=\"cide-element-input-wrapper\">\r\n <!-- Leading Icon -->\r\n <span class=\"cide-input-leading-icon-wrapper\" *ngIf=\"leadingIcon\">\r\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\r\n </span>\r\n <!-- Trailing Icon -->\r\n <span class=\"tw-absolute cide-input-trailing-icon -tw-bottom-1 tw-select-none tw-right-0\"\r\n *ngIf=\"trailingIconInternal\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\r\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\r\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\r\n </span>\r\n <!-- Clear -->\r\n <button class=\"cide-input-clear\" *ngIf=\"clearInput && ngModel\" (click)=\"ClearInputValue()\">\r\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\r\n </button>\r\n <!-- Textarea -->\r\n <textarea [id]=\"id\" [attr.placeholder]=\"placeholder\" [attr.rows]=\"rows\"\r\n [attr.minlength]=\"minlength > 0 ? minlength : null\" [attr.maxlength]=\"maxlength > 0 ? maxlength : null\"\r\n [disabled]=\"disabled\" [(ngModel)]=\"ngModel\" (input)=\"onInput($event)\" (blur)=\"onBlur()\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n class=\"cide-textarea-input tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-py-[1px] tw-outline-none\"></textarea>\r\n </div>\r\n <span *ngIf=\"(errorText || helperText) && !isValid\" class=\"cide-textarea-help-error-text\">{{ errorText || helperText\r\n }}</span>\r\n <span *ngIf=\"helperText && isValid\" class=\"cide-textarea-help-error-text\">{{ helperText }}</span>\r\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
5288
5726
|
}
|
|
5289
5727
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideTextareaComponent, decorators: [{
|
|
5290
5728
|
type: Component,
|
|
@@ -5299,7 +5737,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
5299
5737
|
multi: true,
|
|
5300
5738
|
useExisting: forwardRef(() => CideTextareaComponent),
|
|
5301
5739
|
}
|
|
5302
|
-
], template: "<div class=\"cide-textarea\" [ngClass]=\"{\n 'cide-element-size-xxs': (size === '2xs'),\n 'cide-element-size-xs': (size === 'xs'),\n 'cide-element-size-sm': (size === 'sm'),\n 'cide-element-size-md': (size === 'md'),\n 'cide-element-size-lg': (size === 'lg'),\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\n 'cide-element-input-label-start': (labelDir === 'start'),\n 'cide-element-input-label-end': (labelDir === 'end'),\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\n 'cide-element-input-label-less': (!label || labelHide),\n 'cide-element-style-outline': (fill === 'outline'),\n 'cide-element-style-solid': (fill === 'solid'),\n 'cide-element-style-standard': (fill === 'standard'),\n}\">\n <label *ngIf=\"label && !labelHide\" [for]=\"id\" class=\"cide-textarea-label\">\n {{ label }}\n <span *ngIf=\"required\" class=\"tw-text-red-500 tw-ml-1\">*</span>\n </label>\n <div class=\"cide-element-input-wrapper\">\n <!-- Leading Icon -->\n <span class=\"cide-input-leading-icon-wrapper\" *ngIf=\"leadingIcon\">\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\n </span>\n <!-- Trailing Icon -->\n <span class=\"tw-absolute cide-input-trailing-icon -tw-bottom-1 tw-select-none tw-right-0\"\n *ngIf=\"trailingIconInternal\">\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\n </span>\n <!-- Clear -->\n <button class=\"cide-input-clear\" *ngIf=\"clearInput && ngModel\" (click)=\"ClearInputValue()\">\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\n </button>\n <!-- Textarea -->\n <textarea [id]=\"id\" [attr.placeholder]=\"placeholder\" [attr.rows]=\"rows\"\n [attr.minlength]=\"minlength > 0 ? minlength : null\" [attr.maxlength]=\"maxlength > 0 ? maxlength : null\"\n [disabled]=\"disabled\" [(ngModel)]=\"ngModel\" (input)=\"onInput($event)\" (blur)=\"onBlur()\"\n [ngClass]=\"{\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\n 'tw-pl-1': !leadingIcon,\n 'tw-pr-8': trailingIconInternal,\n 'tw-pr-1': !trailingIconInternal,\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\n '!tw-mt-0': labelHide,\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\n }\"\n class=\"cide-textarea-input tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-py-[1px] tw-outline-none\"></textarea>\n </div>\n <span *ngIf=\"(errorText || helperText) && !isValid\" class=\"cide-textarea-help-error-text\">{{ errorText || helperText\n }}</span>\n <span *ngIf=\"helperText && isValid\" class=\"cide-textarea-help-error-text\">{{ helperText }}</span>\n</div>" }]
|
|
5740
|
+
], template: "<div class=\"cide-textarea\" [ngClass]=\"{\r\n 'cide-element-size-xxs': (size === '2xs'),\r\n 'cide-element-size-xs': (size === 'xs'),\r\n 'cide-element-size-sm': (size === 'sm'),\r\n 'cide-element-size-md': (size === 'md'),\r\n 'cide-element-size-lg': (size === 'lg'),\r\n 'cide-element-input-label-floating': (labelPlacement === 'floating'),\r\n 'cide-element-input-label-start': (labelDir === 'start'),\r\n 'cide-element-input-label-end': (labelDir === 'end'),\r\n 'cide-element-input-label-fixed': (labelPlacement === 'fixed'),\r\n 'cide-element-input-label-less': (!label || labelHide),\r\n 'cide-element-style-outline': (fill === 'outline'),\r\n 'cide-element-style-solid': (fill === 'solid'),\r\n 'cide-element-style-standard': (fill === 'standard'),\r\n}\">\r\n <label *ngIf=\"label && !labelHide\" [for]=\"id\" class=\"cide-textarea-label\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"tw-text-red-500 tw-ml-1\">*</span>\r\n </label>\r\n <div class=\"cide-element-input-wrapper\">\r\n <!-- Leading Icon -->\r\n <span class=\"cide-input-leading-icon-wrapper\" *ngIf=\"leadingIcon\">\r\n <span class=\"cide-input-leading-icon material-symbols-outlined tw-text-center\">{{leadingIcon}}</span>\r\n </span>\r\n <!-- Trailing Icon -->\r\n <span class=\"tw-absolute cide-input-trailing-icon -tw-bottom-1 tw-select-none tw-right-0\"\r\n *ngIf=\"trailingIconInternal\">\r\n <span class=\"material-symbols-outlined tw-w-8 tw-text-center !tw-text-2xl\"\r\n [ngClass]=\"{'tw-cursor-pointer': isTrailingIconAllwedClick}\" [attr.tabindex]=\"false\"\r\n (click)=\"trailingIconClick()\" (keyup)=\"trailingIconClick()\">{{trailingIconInternal}}</span>\r\n </span>\r\n <!-- Clear -->\r\n <button class=\"cide-input-clear\" *ngIf=\"clearInput && ngModel\" (click)=\"ClearInputValue()\">\r\n <span class=\"cide-input-clear-icon material-symbols-outlined\">close</span>\r\n </button>\r\n <!-- Textarea -->\r\n <textarea [id]=\"id\" [attr.placeholder]=\"placeholder\" [attr.rows]=\"rows\"\r\n [attr.minlength]=\"minlength > 0 ? minlength : null\" [attr.maxlength]=\"maxlength > 0 ? maxlength : null\"\r\n [disabled]=\"disabled\" [(ngModel)]=\"ngModel\" (input)=\"onInput($event)\" (blur)=\"onBlur()\"\r\n [ngClass]=\"{\r\n 'tw-rounded-e-md tw-rounded-es-md': label && labelPlacement === 'fixed',\r\n 'tw-rounded-md': !(label && labelPlacement === 'fixed'),\r\n 'tw-pl-1': !leadingIcon,\r\n 'tw-pr-8': trailingIconInternal,\r\n 'tw-pr-1': !trailingIconInternal,\r\n 'tw-h-8 tw-pt-0.5 tw-pb-0': size === 'md',\r\n '!tw-mt-0': labelHide,\r\n 'tw-opacity-50 tw-cursor-not-allowed': disabled\r\n }\"\r\n class=\"cide-textarea-input tw-m-0 tw-w-full tw-bg-transparent tw-overflow-hidden tw-border-solid tw-py-[1px] tw-outline-none\"></textarea>\r\n </div>\r\n <span *ngIf=\"(errorText || helperText) && !isValid\" class=\"cide-textarea-help-error-text\">{{ errorText || helperText\r\n }}</span>\r\n <span *ngIf=\"helperText && isValid\" class=\"cide-textarea-help-error-text\">{{ helperText }}</span>\r\n</div>" }]
|
|
5303
5741
|
}], propDecorators: { label: [{
|
|
5304
5742
|
type: Input
|
|
5305
5743
|
}], labelHide: [{
|
|
@@ -5584,11 +6022,11 @@ class CideEleBreadcrumbComponent {
|
|
|
5584
6022
|
this.localLoading.set(value);
|
|
5585
6023
|
}
|
|
5586
6024
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5587
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleBreadcrumbComponent, isStandalone: true, selector: "cide-ele-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, showHomeIcon: { classPropertyName: "showHomeIcon", publicName: "showHomeIcon", isSignal: true, isRequired: false, transformFunction: null }, homeIcon: { classPropertyName: "homeIcon", publicName: "homeIcon", isSignal: true, isRequired: false, transformFunction: null }, maxItems: { classPropertyName: "maxItems", publicName: "maxItems", isSignal: true, isRequired: false, transformFunction: null }, showDropdownOnOverflow: { classPropertyName: "showDropdownOnOverflow", publicName: "showDropdownOnOverflow", isSignal: true, isRequired: false, transformFunction: null }, dropdownOptions: { classPropertyName: "dropdownOptions", publicName: "dropdownOptions", isSignal: true, isRequired: false, transformFunction: null }, clickableItems: { classPropertyName: "clickableItems", publicName: "clickableItems", isSignal: true, isRequired: false, transformFunction: null }, showTooltips: { classPropertyName: "showTooltips", publicName: "showTooltips", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null }, loadingInput: { classPropertyName: "loadingInput", publicName: "loadingInput", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, contextId: { classPropertyName: "contextId", publicName: "contextId", isSignal: true, isRequired: false, transformFunction: null }, pageCode: { classPropertyName: "pageCode", publicName: "pageCode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick", dropdownOptionClick: "dropdownOptionClick", homeClick: "homeClick" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "<!-- Breadcrumb Component Template -->\n<div [class]=\"getContainerClasses()\" class=\"tw-flex tw-items-center tw-text-xs tw-text-gray-700 tw-py-0\">\n \n <!-- Loading State -->\n @if (effectiveLoading()) {\n <div class=\"breadcrumb-loading\">\n <cide-ele-icon class=\"animate-spin\">refresh</cide-ele-icon>\n <span>Loading...</span>\n </div>\n }\n\n <!-- Main Breadcrumb Content -->\n @if (!effectiveLoading()) {\n <nav class=\"tw-flex tw-items-center tw-p-1 tw-min-w-0 tw-flex-1\" role=\"navigation\" aria-label=\"Breadcrumb\">\n <ol class=\"tw-flex tw-items-center tw-list-none tw-m-0 tw-p-0 tw-gap-1 tw-flex-wrap\">\n \n <!-- Home Icon (if enabled) -->\n @if (config().showHomeIcon) {\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\" \n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\n (click)=\"onHomeClick()\"\n [title]=\"showTooltips() ? 'Go to Home' : ''\">\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs\">\n {{ config().homeIcon }}\n </cide-ele-icon>\n </li>\n \n <!-- Separator after home -->\n @if (visibleItemsSignal().length > 0) {\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\n @if (separator().type === 'custom' && separator().text) {\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\n } @else {\n <cide-ele-icon class=\"tw-text-xs\">\n {{ getSeparatorIcon() }}\n </cide-ele-icon>\n }\n </li>\n }\n }\n\n <!-- Visible Items -->\n @for (item of visibleItemsSignal(); track item.id; let i = $index; let isLast = $last) {\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\"\n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\n [class.tw-text-gray-400]=\"isLast\"\n (click)=\"onItemClick(item)\"\n [title]=\"getTooltipText(item)\"\n [attr.aria-current]=\"isLast ? 'page' : null\">\n \n <!-- Item Icon (if provided) -->\n @if (item.icon) {\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs tw-flex-shrink-0\">\n {{ item.icon }}\n </cide-ele-icon>\n }\n \n <!-- Item Label -->\n <span class=\"tw-font-medium tw-text-xs\">\n {{ item.label }}\n </span>\n \n <!-- Item Type Badge (for hierarchical style) -->\n @if (style() === 'hierarchical' && item.type && item.type !== 'root') {\n <span class=\"item-type-badge\" [class]=\"'type-' + item.type\">\n {{ item.type }}\n </span>\n }\n </li>\n\n <!-- Separator (except for last item) -->\n @if (!isLast || isOverflowingSignal()) {\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\n @if (separator().type === 'custom' && separator().text) {\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\n } @else {\n <cide-ele-icon class=\"tw-text-xs\">\n {{ getSeparatorIcon() }}\n </cide-ele-icon>\n }\n </li>\n }\n }\n\n <!-- Overflow Dropdown -->\n @if (isOverflowingSignal() && showDropdownOnOverflow()) {\n <li class=\"breadcrumb-item overflow-item\">\n <div class=\"breadcrumb-dropdown-container\">\n <button type=\"button\" \n class=\"overflow-button\"\n (click)=\"onDropdownToggle()\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"showDropdownSignal()\"\n aria-haspopup=\"true\"\n [title]=\"showTooltips() ? 'More items' : ''\">\n <cide-ele-icon class=\"overflow-icon\">more_horiz</cide-ele-icon>\n <span class=\"overflow-text\" [class.compact]=\"config().compact\">\n {{ hiddenItemsSignal().length }}+ more\n </span>\n </button>\n \n <!-- Dropdown Menu -->\n @if (showDropdownSignal()) {\n <div class=\"breadcrumb-dropdown\" role=\"menu\">\n <div class=\"dropdown-header\">\n <span>Hidden Items</span>\n </div>\n \n <!-- Hidden Items -->\n @for (item of hiddenItemsSignal(); track item.id) {\n <button type=\"button\" \n class=\"dropdown-item\"\n (click)=\"onItemClick(item)\"\n [disabled]=\"item.disabled || disabled()\"\n role=\"menuitem\">\n @if (item.icon) {\n <cide-ele-icon class=\"dropdown-item-icon\">{{ item.icon }}</cide-ele-icon>\n }\n <span class=\"dropdown-item-label\">{{ item.label }}</span>\n </button>\n }\n \n <!-- Custom Dropdown Options -->\n @if (dropdownOptions().length > 0) {\n <div class=\"dropdown-divider\"></div>\n @for (option of dropdownOptions(); track option.id) {\n <button type=\"button\" \n class=\"dropdown-option\"\n (click)=\"onDropdownOptionClick(option)\"\n [disabled]=\"option.disabled || disabled()\"\n role=\"menuitem\">\n @if (option.icon) {\n <cide-ele-icon class=\"dropdown-option-icon\">{{ option.icon }}</cide-ele-icon>\n }\n <span class=\"dropdown-option-label\">{{ option.label }}</span>\n </button>\n }\n }\n </div>\n }\n </div>\n </li>\n }\n </ol>\n </nav>\n }\n\n</div>\n", styles: [".breadcrumb-container{display:flex;align-items:center;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;line-height:1.3;color:#374151;background:transparent;padding:2px 0}.breadcrumb-container.loading{opacity:.6;pointer-events:none}.breadcrumb-container.disabled{opacity:.5;pointer-events:none}@media (max-width: 768px){.breadcrumb-container.responsive{font-size:10px;padding:3px 0}}.breadcrumb-container.compact{font-size:10px;padding:1px 0}.breadcrumb-container.animated .breadcrumb-item{transition:color .15s ease}.breadcrumb-loading{display:flex;align-items:center;gap:8px;padding:12px 16px;color:#6b7280;font-size:14px}.breadcrumb-loading cide-ele-icon{animation:spin 1s linear infinite}.breadcrumb-nav{flex:1;min-width:0}.breadcrumb-list{display:flex;align-items:center;list-style:none;margin:0;padding:0;gap:4px;flex-wrap:wrap}.breadcrumb-item{display:flex;align-items:center;gap:2px;padding:0 2px;position:relative;white-space:nowrap}.breadcrumb-item.clickable{cursor:pointer}.breadcrumb-item.clickable:hover{color:#1f2937}.breadcrumb-item.disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.breadcrumb-item.last{color:#9ca3af}.breadcrumb-item.home-item{padding:0 1px}.breadcrumb-item.home-item .home-icon{color:#6b7280;font-size:13px}.breadcrumb-item.overflow-item{position:relative}.item-label{font-weight:500;color:inherit}.item-label.compact{font-weight:600}.item-icon{color:#6b7280;flex-shrink:0}.item-type-badge{padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.item-type-badge.type-category{background-color:#dbeafe;color:#1e40af}.item-type-badge.type-entity{background-color:#dcfce7;color:#166534}.item-type-badge.type-custom{background-color:#f3e8ff;color:#7c3aed}.breadcrumb-separator{display:flex;align-items:center;color:#d1d5db;margin:0 .5px}.breadcrumb-separator .separator-icon{font-size:11px}.breadcrumb-separator .custom-separator{font-weight:400;color:#9ca3af}.breadcrumb-dropdown-container{position:relative}.overflow-button{display:flex;align-items:center;gap:2px;padding:2px 4px;border:none;background:transparent;cursor:pointer;transition:color .15s ease;color:#6b7280}.overflow-button:hover{color:#374151}.overflow-button:disabled{opacity:.5;cursor:not-allowed}.overflow-icon{font-size:14px}.overflow-text{font-size:11px;font-weight:400}.breadcrumb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;z-index:1000;min-width:200px;max-width:300px;overflow:hidden;animation:dropdownSlideIn .2s ease-out}.dropdown-header{padding:8px 12px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.5px}.dropdown-item,.dropdown-option{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:none;background:transparent;text-align:left;cursor:pointer;transition:background-color .15s ease;color:#374151}.dropdown-item:hover,.dropdown-option:hover{background:#f3f4f6}.dropdown-item:disabled,.dropdown-option:disabled{opacity:.5;cursor:not-allowed}.dropdown-item-icon,.dropdown-option-icon{font-size:14px;color:#6b7280;flex-shrink:0}.dropdown-item-label,.dropdown-option-label{font-size:14px;font-weight:500}.dropdown-divider{height:1px;background:#e5e7eb;margin:4px 0}.style-modern .breadcrumb-item.clickable:hover{color:#1f2937}.style-modern .separator-icon{color:#d1d5db}.style-classic .breadcrumb-item{padding:2px 4px}.style-classic .breadcrumb-item.clickable:hover{color:#1f2937}.style-classic .separator-icon{color:#9ca3af;font-size:12px}.style-minimal .breadcrumb-item{padding:1px 2px}.style-minimal .breadcrumb-item.clickable:hover{color:#1f2937}.style-minimal .separator-icon{color:#d1d5db;font-size:10px}.style-hierarchical{background:transparent;border:none;padding:2px 0}.style-hierarchical .breadcrumb-item{padding:0 2px}.style-hierarchical .breadcrumb-item.clickable:hover{color:#1f2937}.style-hierarchical .separator-icon{color:#d1d5db}.style-hierarchical .breadcrumb-actions{display:none}@keyframes dropdownSlideIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.breadcrumb-container.animated .breadcrumb-item{transition:none}.breadcrumb-container.animated .breadcrumb-item:hover{transform:none}.breadcrumb-dropdown{animation:none}}@media (prefers-contrast: high){.breadcrumb-container .breadcrumb-item{border:1px solid transparent}.breadcrumb-container .breadcrumb-item.clickable:hover{border-color:#000}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6025
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleBreadcrumbComponent, isStandalone: true, selector: "cide-ele-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, showHomeIcon: { classPropertyName: "showHomeIcon", publicName: "showHomeIcon", isSignal: true, isRequired: false, transformFunction: null }, homeIcon: { classPropertyName: "homeIcon", publicName: "homeIcon", isSignal: true, isRequired: false, transformFunction: null }, maxItems: { classPropertyName: "maxItems", publicName: "maxItems", isSignal: true, isRequired: false, transformFunction: null }, showDropdownOnOverflow: { classPropertyName: "showDropdownOnOverflow", publicName: "showDropdownOnOverflow", isSignal: true, isRequired: false, transformFunction: null }, dropdownOptions: { classPropertyName: "dropdownOptions", publicName: "dropdownOptions", isSignal: true, isRequired: false, transformFunction: null }, clickableItems: { classPropertyName: "clickableItems", publicName: "clickableItems", isSignal: true, isRequired: false, transformFunction: null }, showTooltips: { classPropertyName: "showTooltips", publicName: "showTooltips", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null }, loadingInput: { classPropertyName: "loadingInput", publicName: "loadingInput", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, contextId: { classPropertyName: "contextId", publicName: "contextId", isSignal: true, isRequired: false, transformFunction: null }, pageCode: { classPropertyName: "pageCode", publicName: "pageCode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick", dropdownOptionClick: "dropdownOptionClick", homeClick: "homeClick" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "<!-- Breadcrumb Component Template -->\r\n<div [class]=\"getContainerClasses()\" class=\"tw-flex tw-items-center tw-text-xs tw-text-gray-700 tw-py-0\">\r\n \r\n <!-- Loading State -->\r\n @if (effectiveLoading()) {\r\n <div class=\"breadcrumb-loading\">\r\n <cide-ele-icon class=\"animate-spin\">refresh</cide-ele-icon>\r\n <span>Loading...</span>\r\n </div>\r\n }\r\n\r\n <!-- Main Breadcrumb Content -->\r\n @if (!effectiveLoading()) {\r\n <nav class=\"tw-flex tw-items-center tw-p-1 tw-min-w-0 tw-flex-1\" role=\"navigation\" aria-label=\"Breadcrumb\">\r\n <ol class=\"tw-flex tw-items-center tw-list-none tw-m-0 tw-p-0 tw-gap-1 tw-flex-wrap\">\r\n \r\n <!-- Home Icon (if enabled) -->\r\n @if (config().showHomeIcon) {\r\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\" \r\n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\r\n (click)=\"onHomeClick()\"\r\n [title]=\"showTooltips() ? 'Go to Home' : ''\">\r\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs\">\r\n {{ config().homeIcon }}\r\n </cide-ele-icon>\r\n </li>\r\n \r\n <!-- Separator after home -->\r\n @if (visibleItemsSignal().length > 0) {\r\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\r\n @if (separator().type === 'custom' && separator().text) {\r\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\r\n } @else {\r\n <cide-ele-icon class=\"tw-text-xs\">\r\n {{ getSeparatorIcon() }}\r\n </cide-ele-icon>\r\n }\r\n </li>\r\n }\r\n }\r\n\r\n <!-- Visible Items -->\r\n @for (item of visibleItemsSignal(); track item.id; let i = $index; let isLast = $last) {\r\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\"\r\n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\r\n [class.tw-text-gray-400]=\"isLast\"\r\n (click)=\"onItemClick(item)\"\r\n [title]=\"getTooltipText(item)\"\r\n [attr.aria-current]=\"isLast ? 'page' : null\">\r\n \r\n <!-- Item Icon (if provided) -->\r\n @if (item.icon) {\r\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs tw-flex-shrink-0\">\r\n {{ item.icon }}\r\n </cide-ele-icon>\r\n }\r\n \r\n <!-- Item Label -->\r\n <span class=\"tw-font-medium tw-text-xs\">\r\n {{ item.label }}\r\n </span>\r\n \r\n <!-- Item Type Badge (for hierarchical style) -->\r\n @if (style() === 'hierarchical' && item.type && item.type !== 'root') {\r\n <span class=\"item-type-badge\" [class]=\"'type-' + item.type\">\r\n {{ item.type }}\r\n </span>\r\n }\r\n </li>\r\n\r\n <!-- Separator (except for last item) -->\r\n @if (!isLast || isOverflowingSignal()) {\r\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\r\n @if (separator().type === 'custom' && separator().text) {\r\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\r\n } @else {\r\n <cide-ele-icon class=\"tw-text-xs\">\r\n {{ getSeparatorIcon() }}\r\n </cide-ele-icon>\r\n }\r\n </li>\r\n }\r\n }\r\n\r\n <!-- Overflow Dropdown -->\r\n @if (isOverflowingSignal() && showDropdownOnOverflow()) {\r\n <li class=\"breadcrumb-item overflow-item\">\r\n <div class=\"breadcrumb-dropdown-container\">\r\n <button type=\"button\" \r\n class=\"overflow-button\"\r\n (click)=\"onDropdownToggle()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"showDropdownSignal()\"\r\n aria-haspopup=\"true\"\r\n [title]=\"showTooltips() ? 'More items' : ''\">\r\n <cide-ele-icon class=\"overflow-icon\">more_horiz</cide-ele-icon>\r\n <span class=\"overflow-text\" [class.compact]=\"config().compact\">\r\n {{ hiddenItemsSignal().length }}+ more\r\n </span>\r\n </button>\r\n \r\n <!-- Dropdown Menu -->\r\n @if (showDropdownSignal()) {\r\n <div class=\"breadcrumb-dropdown\" role=\"menu\">\r\n <div class=\"dropdown-header\">\r\n <span>Hidden Items</span>\r\n </div>\r\n \r\n <!-- Hidden Items -->\r\n @for (item of hiddenItemsSignal(); track item.id) {\r\n <button type=\"button\" \r\n class=\"dropdown-item\"\r\n (click)=\"onItemClick(item)\"\r\n [disabled]=\"item.disabled || disabled()\"\r\n role=\"menuitem\">\r\n @if (item.icon) {\r\n <cide-ele-icon class=\"dropdown-item-icon\">{{ item.icon }}</cide-ele-icon>\r\n }\r\n <span class=\"dropdown-item-label\">{{ item.label }}</span>\r\n </button>\r\n }\r\n \r\n <!-- Custom Dropdown Options -->\r\n @if (dropdownOptions().length > 0) {\r\n <div class=\"dropdown-divider\"></div>\r\n @for (option of dropdownOptions(); track option.id) {\r\n <button type=\"button\" \r\n class=\"dropdown-option\"\r\n (click)=\"onDropdownOptionClick(option)\"\r\n [disabled]=\"option.disabled || disabled()\"\r\n role=\"menuitem\">\r\n @if (option.icon) {\r\n <cide-ele-icon class=\"dropdown-option-icon\">{{ option.icon }}</cide-ele-icon>\r\n }\r\n <span class=\"dropdown-option-label\">{{ option.label }}</span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </li>\r\n }\r\n </ol>\r\n </nav>\r\n }\r\n\r\n</div>\r\n", styles: [".breadcrumb-container{display:flex;align-items:center;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;line-height:1.3;color:#374151;background:transparent;padding:2px 0}.breadcrumb-container.loading{opacity:.6;pointer-events:none}.breadcrumb-container.disabled{opacity:.5;pointer-events:none}@media (max-width: 768px){.breadcrumb-container.responsive{font-size:10px;padding:3px 0}}.breadcrumb-container.compact{font-size:10px;padding:1px 0}.breadcrumb-container.animated .breadcrumb-item{transition:color .15s ease}.breadcrumb-loading{display:flex;align-items:center;gap:8px;padding:12px 16px;color:#6b7280;font-size:14px}.breadcrumb-loading cide-ele-icon{animation:spin 1s linear infinite}.breadcrumb-nav{flex:1;min-width:0}.breadcrumb-list{display:flex;align-items:center;list-style:none;margin:0;padding:0;gap:4px;flex-wrap:wrap}.breadcrumb-item{display:flex;align-items:center;gap:2px;padding:0 2px;position:relative;white-space:nowrap}.breadcrumb-item.clickable{cursor:pointer}.breadcrumb-item.clickable:hover{color:#1f2937}.breadcrumb-item.disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.breadcrumb-item.last{color:#9ca3af}.breadcrumb-item.home-item{padding:0 1px}.breadcrumb-item.home-item .home-icon{color:#6b7280;font-size:13px}.breadcrumb-item.overflow-item{position:relative}.item-label{font-weight:500;color:inherit}.item-label.compact{font-weight:600}.item-icon{color:#6b7280;flex-shrink:0}.item-type-badge{padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.item-type-badge.type-category{background-color:#dbeafe;color:#1e40af}.item-type-badge.type-entity{background-color:#dcfce7;color:#166534}.item-type-badge.type-custom{background-color:#f3e8ff;color:#7c3aed}.breadcrumb-separator{display:flex;align-items:center;color:#d1d5db;margin:0 .5px}.breadcrumb-separator .separator-icon{font-size:11px}.breadcrumb-separator .custom-separator{font-weight:400;color:#9ca3af}.breadcrumb-dropdown-container{position:relative}.overflow-button{display:flex;align-items:center;gap:2px;padding:2px 4px;border:none;background:transparent;cursor:pointer;transition:color .15s ease;color:#6b7280}.overflow-button:hover{color:#374151}.overflow-button:disabled{opacity:.5;cursor:not-allowed}.overflow-icon{font-size:14px}.overflow-text{font-size:11px;font-weight:400}.breadcrumb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;z-index:1000;min-width:200px;max-width:300px;overflow:hidden;animation:dropdownSlideIn .2s ease-out}.dropdown-header{padding:8px 12px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.5px}.dropdown-item,.dropdown-option{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:none;background:transparent;text-align:left;cursor:pointer;transition:background-color .15s ease;color:#374151}.dropdown-item:hover,.dropdown-option:hover{background:#f3f4f6}.dropdown-item:disabled,.dropdown-option:disabled{opacity:.5;cursor:not-allowed}.dropdown-item-icon,.dropdown-option-icon{font-size:14px;color:#6b7280;flex-shrink:0}.dropdown-item-label,.dropdown-option-label{font-size:14px;font-weight:500}.dropdown-divider{height:1px;background:#e5e7eb;margin:4px 0}.style-modern .breadcrumb-item.clickable:hover{color:#1f2937}.style-modern .separator-icon{color:#d1d5db}.style-classic .breadcrumb-item{padding:2px 4px}.style-classic .breadcrumb-item.clickable:hover{color:#1f2937}.style-classic .separator-icon{color:#9ca3af;font-size:12px}.style-minimal .breadcrumb-item{padding:1px 2px}.style-minimal .breadcrumb-item.clickable:hover{color:#1f2937}.style-minimal .separator-icon{color:#d1d5db;font-size:10px}.style-hierarchical{background:transparent;border:none;padding:2px 0}.style-hierarchical .breadcrumb-item{padding:0 2px}.style-hierarchical .breadcrumb-item.clickable:hover{color:#1f2937}.style-hierarchical .separator-icon{color:#d1d5db}.style-hierarchical .breadcrumb-actions{display:none}@keyframes dropdownSlideIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.breadcrumb-container.animated .breadcrumb-item{transition:none}.breadcrumb-container.animated .breadcrumb-item:hover{transform:none}.breadcrumb-dropdown{animation:none}}@media (prefers-contrast: high){.breadcrumb-container .breadcrumb-item{border:1px solid transparent}.breadcrumb-container .breadcrumb-item.clickable:hover{border-color:#000}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5588
6026
|
}
|
|
5589
6027
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleBreadcrumbComponent, decorators: [{
|
|
5590
6028
|
type: Component,
|
|
5591
|
-
args: [{ selector: 'cide-ele-breadcrumb', standalone: true, imports: [CommonModule, CideIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Breadcrumb Component Template -->\n<div [class]=\"getContainerClasses()\" class=\"tw-flex tw-items-center tw-text-xs tw-text-gray-700 tw-py-0\">\n \n <!-- Loading State -->\n @if (effectiveLoading()) {\n <div class=\"breadcrumb-loading\">\n <cide-ele-icon class=\"animate-spin\">refresh</cide-ele-icon>\n <span>Loading...</span>\n </div>\n }\n\n <!-- Main Breadcrumb Content -->\n @if (!effectiveLoading()) {\n <nav class=\"tw-flex tw-items-center tw-p-1 tw-min-w-0 tw-flex-1\" role=\"navigation\" aria-label=\"Breadcrumb\">\n <ol class=\"tw-flex tw-items-center tw-list-none tw-m-0 tw-p-0 tw-gap-1 tw-flex-wrap\">\n \n <!-- Home Icon (if enabled) -->\n @if (config().showHomeIcon) {\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\" \n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\n (click)=\"onHomeClick()\"\n [title]=\"showTooltips() ? 'Go to Home' : ''\">\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs\">\n {{ config().homeIcon }}\n </cide-ele-icon>\n </li>\n \n <!-- Separator after home -->\n @if (visibleItemsSignal().length > 0) {\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\n @if (separator().type === 'custom' && separator().text) {\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\n } @else {\n <cide-ele-icon class=\"tw-text-xs\">\n {{ getSeparatorIcon() }}\n </cide-ele-icon>\n }\n </li>\n }\n }\n\n <!-- Visible Items -->\n @for (item of visibleItemsSignal(); track item.id; let i = $index; let isLast = $last) {\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\"\n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\n [class.tw-text-gray-400]=\"isLast\"\n (click)=\"onItemClick(item)\"\n [title]=\"getTooltipText(item)\"\n [attr.aria-current]=\"isLast ? 'page' : null\">\n \n <!-- Item Icon (if provided) -->\n @if (item.icon) {\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs tw-flex-shrink-0\">\n {{ item.icon }}\n </cide-ele-icon>\n }\n \n <!-- Item Label -->\n <span class=\"tw-font-medium tw-text-xs\">\n {{ item.label }}\n </span>\n \n <!-- Item Type Badge (for hierarchical style) -->\n @if (style() === 'hierarchical' && item.type && item.type !== 'root') {\n <span class=\"item-type-badge\" [class]=\"'type-' + item.type\">\n {{ item.type }}\n </span>\n }\n </li>\n\n <!-- Separator (except for last item) -->\n @if (!isLast || isOverflowingSignal()) {\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\n @if (separator().type === 'custom' && separator().text) {\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\n } @else {\n <cide-ele-icon class=\"tw-text-xs\">\n {{ getSeparatorIcon() }}\n </cide-ele-icon>\n }\n </li>\n }\n }\n\n <!-- Overflow Dropdown -->\n @if (isOverflowingSignal() && showDropdownOnOverflow()) {\n <li class=\"breadcrumb-item overflow-item\">\n <div class=\"breadcrumb-dropdown-container\">\n <button type=\"button\" \n class=\"overflow-button\"\n (click)=\"onDropdownToggle()\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"showDropdownSignal()\"\n aria-haspopup=\"true\"\n [title]=\"showTooltips() ? 'More items' : ''\">\n <cide-ele-icon class=\"overflow-icon\">more_horiz</cide-ele-icon>\n <span class=\"overflow-text\" [class.compact]=\"config().compact\">\n {{ hiddenItemsSignal().length }}+ more\n </span>\n </button>\n \n <!-- Dropdown Menu -->\n @if (showDropdownSignal()) {\n <div class=\"breadcrumb-dropdown\" role=\"menu\">\n <div class=\"dropdown-header\">\n <span>Hidden Items</span>\n </div>\n \n <!-- Hidden Items -->\n @for (item of hiddenItemsSignal(); track item.id) {\n <button type=\"button\" \n class=\"dropdown-item\"\n (click)=\"onItemClick(item)\"\n [disabled]=\"item.disabled || disabled()\"\n role=\"menuitem\">\n @if (item.icon) {\n <cide-ele-icon class=\"dropdown-item-icon\">{{ item.icon }}</cide-ele-icon>\n }\n <span class=\"dropdown-item-label\">{{ item.label }}</span>\n </button>\n }\n \n <!-- Custom Dropdown Options -->\n @if (dropdownOptions().length > 0) {\n <div class=\"dropdown-divider\"></div>\n @for (option of dropdownOptions(); track option.id) {\n <button type=\"button\" \n class=\"dropdown-option\"\n (click)=\"onDropdownOptionClick(option)\"\n [disabled]=\"option.disabled || disabled()\"\n role=\"menuitem\">\n @if (option.icon) {\n <cide-ele-icon class=\"dropdown-option-icon\">{{ option.icon }}</cide-ele-icon>\n }\n <span class=\"dropdown-option-label\">{{ option.label }}</span>\n </button>\n }\n }\n </div>\n }\n </div>\n </li>\n }\n </ol>\n </nav>\n }\n\n</div>\n", styles: [".breadcrumb-container{display:flex;align-items:center;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;line-height:1.3;color:#374151;background:transparent;padding:2px 0}.breadcrumb-container.loading{opacity:.6;pointer-events:none}.breadcrumb-container.disabled{opacity:.5;pointer-events:none}@media (max-width: 768px){.breadcrumb-container.responsive{font-size:10px;padding:3px 0}}.breadcrumb-container.compact{font-size:10px;padding:1px 0}.breadcrumb-container.animated .breadcrumb-item{transition:color .15s ease}.breadcrumb-loading{display:flex;align-items:center;gap:8px;padding:12px 16px;color:#6b7280;font-size:14px}.breadcrumb-loading cide-ele-icon{animation:spin 1s linear infinite}.breadcrumb-nav{flex:1;min-width:0}.breadcrumb-list{display:flex;align-items:center;list-style:none;margin:0;padding:0;gap:4px;flex-wrap:wrap}.breadcrumb-item{display:flex;align-items:center;gap:2px;padding:0 2px;position:relative;white-space:nowrap}.breadcrumb-item.clickable{cursor:pointer}.breadcrumb-item.clickable:hover{color:#1f2937}.breadcrumb-item.disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.breadcrumb-item.last{color:#9ca3af}.breadcrumb-item.home-item{padding:0 1px}.breadcrumb-item.home-item .home-icon{color:#6b7280;font-size:13px}.breadcrumb-item.overflow-item{position:relative}.item-label{font-weight:500;color:inherit}.item-label.compact{font-weight:600}.item-icon{color:#6b7280;flex-shrink:0}.item-type-badge{padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.item-type-badge.type-category{background-color:#dbeafe;color:#1e40af}.item-type-badge.type-entity{background-color:#dcfce7;color:#166534}.item-type-badge.type-custom{background-color:#f3e8ff;color:#7c3aed}.breadcrumb-separator{display:flex;align-items:center;color:#d1d5db;margin:0 .5px}.breadcrumb-separator .separator-icon{font-size:11px}.breadcrumb-separator .custom-separator{font-weight:400;color:#9ca3af}.breadcrumb-dropdown-container{position:relative}.overflow-button{display:flex;align-items:center;gap:2px;padding:2px 4px;border:none;background:transparent;cursor:pointer;transition:color .15s ease;color:#6b7280}.overflow-button:hover{color:#374151}.overflow-button:disabled{opacity:.5;cursor:not-allowed}.overflow-icon{font-size:14px}.overflow-text{font-size:11px;font-weight:400}.breadcrumb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;z-index:1000;min-width:200px;max-width:300px;overflow:hidden;animation:dropdownSlideIn .2s ease-out}.dropdown-header{padding:8px 12px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.5px}.dropdown-item,.dropdown-option{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:none;background:transparent;text-align:left;cursor:pointer;transition:background-color .15s ease;color:#374151}.dropdown-item:hover,.dropdown-option:hover{background:#f3f4f6}.dropdown-item:disabled,.dropdown-option:disabled{opacity:.5;cursor:not-allowed}.dropdown-item-icon,.dropdown-option-icon{font-size:14px;color:#6b7280;flex-shrink:0}.dropdown-item-label,.dropdown-option-label{font-size:14px;font-weight:500}.dropdown-divider{height:1px;background:#e5e7eb;margin:4px 0}.style-modern .breadcrumb-item.clickable:hover{color:#1f2937}.style-modern .separator-icon{color:#d1d5db}.style-classic .breadcrumb-item{padding:2px 4px}.style-classic .breadcrumb-item.clickable:hover{color:#1f2937}.style-classic .separator-icon{color:#9ca3af;font-size:12px}.style-minimal .breadcrumb-item{padding:1px 2px}.style-minimal .breadcrumb-item.clickable:hover{color:#1f2937}.style-minimal .separator-icon{color:#d1d5db;font-size:10px}.style-hierarchical{background:transparent;border:none;padding:2px 0}.style-hierarchical .breadcrumb-item{padding:0 2px}.style-hierarchical .breadcrumb-item.clickable:hover{color:#1f2937}.style-hierarchical .separator-icon{color:#d1d5db}.style-hierarchical .breadcrumb-actions{display:none}@keyframes dropdownSlideIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.breadcrumb-container.animated .breadcrumb-item{transition:none}.breadcrumb-container.animated .breadcrumb-item:hover{transform:none}.breadcrumb-dropdown{animation:none}}@media (prefers-contrast: high){.breadcrumb-container .breadcrumb-item{border:1px solid transparent}.breadcrumb-container .breadcrumb-item.clickable:hover{border-color:#000}}\n"] }]
|
|
6029
|
+
args: [{ selector: 'cide-ele-breadcrumb', standalone: true, imports: [CommonModule, CideIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Breadcrumb Component Template -->\r\n<div [class]=\"getContainerClasses()\" class=\"tw-flex tw-items-center tw-text-xs tw-text-gray-700 tw-py-0\">\r\n \r\n <!-- Loading State -->\r\n @if (effectiveLoading()) {\r\n <div class=\"breadcrumb-loading\">\r\n <cide-ele-icon class=\"animate-spin\">refresh</cide-ele-icon>\r\n <span>Loading...</span>\r\n </div>\r\n }\r\n\r\n <!-- Main Breadcrumb Content -->\r\n @if (!effectiveLoading()) {\r\n <nav class=\"tw-flex tw-items-center tw-p-1 tw-min-w-0 tw-flex-1\" role=\"navigation\" aria-label=\"Breadcrumb\">\r\n <ol class=\"tw-flex tw-items-center tw-list-none tw-m-0 tw-p-0 tw-gap-1 tw-flex-wrap\">\r\n \r\n <!-- Home Icon (if enabled) -->\r\n @if (config().showHomeIcon) {\r\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\" \r\n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\r\n (click)=\"onHomeClick()\"\r\n [title]=\"showTooltips() ? 'Go to Home' : ''\">\r\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs\">\r\n {{ config().homeIcon }}\r\n </cide-ele-icon>\r\n </li>\r\n \r\n <!-- Separator after home -->\r\n @if (visibleItemsSignal().length > 0) {\r\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\r\n @if (separator().type === 'custom' && separator().text) {\r\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\r\n } @else {\r\n <cide-ele-icon class=\"tw-text-xs\">\r\n {{ getSeparatorIcon() }}\r\n </cide-ele-icon>\r\n }\r\n </li>\r\n }\r\n }\r\n\r\n <!-- Visible Items -->\r\n @for (item of visibleItemsSignal(); track item.id; let i = $index; let isLast = $last) {\r\n <li class=\"tw-flex tw-items-center tw-gap-1 tw-px-1 tw-whitespace-nowrap tw-relative\"\r\n [class.tw-cursor-pointer]=\"clickableItems() && !disabled()\"\r\n [class.tw-text-gray-400]=\"isLast\"\r\n (click)=\"onItemClick(item)\"\r\n [title]=\"getTooltipText(item)\"\r\n [attr.aria-current]=\"isLast ? 'page' : null\">\r\n \r\n <!-- Item Icon (if provided) -->\r\n @if (item.icon) {\r\n <cide-ele-icon class=\"tw-text-gray-500 tw-text-xs tw-flex-shrink-0\">\r\n {{ item.icon }}\r\n </cide-ele-icon>\r\n }\r\n \r\n <!-- Item Label -->\r\n <span class=\"tw-font-medium tw-text-xs\">\r\n {{ item.label }}\r\n </span>\r\n \r\n <!-- Item Type Badge (for hierarchical style) -->\r\n @if (style() === 'hierarchical' && item.type && item.type !== 'root') {\r\n <span class=\"item-type-badge\" [class]=\"'type-' + item.type\">\r\n {{ item.type }}\r\n </span>\r\n }\r\n </li>\r\n\r\n <!-- Separator (except for last item) -->\r\n @if (!isLast || isOverflowingSignal()) {\r\n <li class=\"tw-flex tw-items-center tw-text-gray-300 tw-mx-0.5\">\r\n @if (separator().type === 'custom' && separator().text) {\r\n <span class=\"tw-font-normal tw-text-gray-400\">{{ separator().text }}</span>\r\n } @else {\r\n <cide-ele-icon class=\"tw-text-xs\">\r\n {{ getSeparatorIcon() }}\r\n </cide-ele-icon>\r\n }\r\n </li>\r\n }\r\n }\r\n\r\n <!-- Overflow Dropdown -->\r\n @if (isOverflowingSignal() && showDropdownOnOverflow()) {\r\n <li class=\"breadcrumb-item overflow-item\">\r\n <div class=\"breadcrumb-dropdown-container\">\r\n <button type=\"button\" \r\n class=\"overflow-button\"\r\n (click)=\"onDropdownToggle()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"showDropdownSignal()\"\r\n aria-haspopup=\"true\"\r\n [title]=\"showTooltips() ? 'More items' : ''\">\r\n <cide-ele-icon class=\"overflow-icon\">more_horiz</cide-ele-icon>\r\n <span class=\"overflow-text\" [class.compact]=\"config().compact\">\r\n {{ hiddenItemsSignal().length }}+ more\r\n </span>\r\n </button>\r\n \r\n <!-- Dropdown Menu -->\r\n @if (showDropdownSignal()) {\r\n <div class=\"breadcrumb-dropdown\" role=\"menu\">\r\n <div class=\"dropdown-header\">\r\n <span>Hidden Items</span>\r\n </div>\r\n \r\n <!-- Hidden Items -->\r\n @for (item of hiddenItemsSignal(); track item.id) {\r\n <button type=\"button\" \r\n class=\"dropdown-item\"\r\n (click)=\"onItemClick(item)\"\r\n [disabled]=\"item.disabled || disabled()\"\r\n role=\"menuitem\">\r\n @if (item.icon) {\r\n <cide-ele-icon class=\"dropdown-item-icon\">{{ item.icon }}</cide-ele-icon>\r\n }\r\n <span class=\"dropdown-item-label\">{{ item.label }}</span>\r\n </button>\r\n }\r\n \r\n <!-- Custom Dropdown Options -->\r\n @if (dropdownOptions().length > 0) {\r\n <div class=\"dropdown-divider\"></div>\r\n @for (option of dropdownOptions(); track option.id) {\r\n <button type=\"button\" \r\n class=\"dropdown-option\"\r\n (click)=\"onDropdownOptionClick(option)\"\r\n [disabled]=\"option.disabled || disabled()\"\r\n role=\"menuitem\">\r\n @if (option.icon) {\r\n <cide-ele-icon class=\"dropdown-option-icon\">{{ option.icon }}</cide-ele-icon>\r\n }\r\n <span class=\"dropdown-option-label\">{{ option.label }}</span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </li>\r\n }\r\n </ol>\r\n </nav>\r\n }\r\n\r\n</div>\r\n", styles: [".breadcrumb-container{display:flex;align-items:center;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;line-height:1.3;color:#374151;background:transparent;padding:2px 0}.breadcrumb-container.loading{opacity:.6;pointer-events:none}.breadcrumb-container.disabled{opacity:.5;pointer-events:none}@media (max-width: 768px){.breadcrumb-container.responsive{font-size:10px;padding:3px 0}}.breadcrumb-container.compact{font-size:10px;padding:1px 0}.breadcrumb-container.animated .breadcrumb-item{transition:color .15s ease}.breadcrumb-loading{display:flex;align-items:center;gap:8px;padding:12px 16px;color:#6b7280;font-size:14px}.breadcrumb-loading cide-ele-icon{animation:spin 1s linear infinite}.breadcrumb-nav{flex:1;min-width:0}.breadcrumb-list{display:flex;align-items:center;list-style:none;margin:0;padding:0;gap:4px;flex-wrap:wrap}.breadcrumb-item{display:flex;align-items:center;gap:2px;padding:0 2px;position:relative;white-space:nowrap}.breadcrumb-item.clickable{cursor:pointer}.breadcrumb-item.clickable:hover{color:#1f2937}.breadcrumb-item.disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.breadcrumb-item.last{color:#9ca3af}.breadcrumb-item.home-item{padding:0 1px}.breadcrumb-item.home-item .home-icon{color:#6b7280;font-size:13px}.breadcrumb-item.overflow-item{position:relative}.item-label{font-weight:500;color:inherit}.item-label.compact{font-weight:600}.item-icon{color:#6b7280;flex-shrink:0}.item-type-badge{padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.item-type-badge.type-category{background-color:#dbeafe;color:#1e40af}.item-type-badge.type-entity{background-color:#dcfce7;color:#166534}.item-type-badge.type-custom{background-color:#f3e8ff;color:#7c3aed}.breadcrumb-separator{display:flex;align-items:center;color:#d1d5db;margin:0 .5px}.breadcrumb-separator .separator-icon{font-size:11px}.breadcrumb-separator .custom-separator{font-weight:400;color:#9ca3af}.breadcrumb-dropdown-container{position:relative}.overflow-button{display:flex;align-items:center;gap:2px;padding:2px 4px;border:none;background:transparent;cursor:pointer;transition:color .15s ease;color:#6b7280}.overflow-button:hover{color:#374151}.overflow-button:disabled{opacity:.5;cursor:not-allowed}.overflow-icon{font-size:14px}.overflow-text{font-size:11px;font-weight:400}.breadcrumb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;z-index:1000;min-width:200px;max-width:300px;overflow:hidden;animation:dropdownSlideIn .2s ease-out}.dropdown-header{padding:8px 12px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.5px}.dropdown-item,.dropdown-option{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:none;background:transparent;text-align:left;cursor:pointer;transition:background-color .15s ease;color:#374151}.dropdown-item:hover,.dropdown-option:hover{background:#f3f4f6}.dropdown-item:disabled,.dropdown-option:disabled{opacity:.5;cursor:not-allowed}.dropdown-item-icon,.dropdown-option-icon{font-size:14px;color:#6b7280;flex-shrink:0}.dropdown-item-label,.dropdown-option-label{font-size:14px;font-weight:500}.dropdown-divider{height:1px;background:#e5e7eb;margin:4px 0}.style-modern .breadcrumb-item.clickable:hover{color:#1f2937}.style-modern .separator-icon{color:#d1d5db}.style-classic .breadcrumb-item{padding:2px 4px}.style-classic .breadcrumb-item.clickable:hover{color:#1f2937}.style-classic .separator-icon{color:#9ca3af;font-size:12px}.style-minimal .breadcrumb-item{padding:1px 2px}.style-minimal .breadcrumb-item.clickable:hover{color:#1f2937}.style-minimal .separator-icon{color:#d1d5db;font-size:10px}.style-hierarchical{background:transparent;border:none;padding:2px 0}.style-hierarchical .breadcrumb-item{padding:0 2px}.style-hierarchical .breadcrumb-item.clickable:hover{color:#1f2937}.style-hierarchical .separator-icon{color:#d1d5db}.style-hierarchical .breadcrumb-actions{display:none}@keyframes dropdownSlideIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.breadcrumb-container.animated .breadcrumb-item{transition:none}.breadcrumb-container.animated .breadcrumb-item:hover{transform:none}.breadcrumb-dropdown{animation:none}}@media (prefers-contrast: high){.breadcrumb-container .breadcrumb-item{border:1px solid transparent}.breadcrumb-container .breadcrumb-item.clickable:hover{border-color:#000}}\n"] }]
|
|
5592
6030
|
}], ctorParameters: () => [], propDecorators: { itemClick: [{
|
|
5593
6031
|
type: Output
|
|
5594
6032
|
}], dropdownOptionClick: [{
|
|
@@ -7306,24 +7744,24 @@ class CideFormFieldErrorComponent {
|
|
|
7306
7744
|
.join(' ');
|
|
7307
7745
|
}
|
|
7308
7746
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideFormFieldErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7309
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideFormFieldErrorComponent, isStandalone: true, selector: "cide-form-field-error", inputs: { control: "control", formGroup: "formGroup", fieldName: "fieldName", customMessages: "customMessages" }, usesOnChanges: true, ngImport: i0, template: `
|
|
7310
|
-
@if (isFormGroupMode()) {
|
|
7311
|
-
<!-- Form Group Error Display -->
|
|
7312
|
-
@if (formErrors().length > 0) {
|
|
7313
|
-
<div class="tw-flex tw-items-center tw-gap-2 tw-text-red-600 tw-text-sm">
|
|
7314
|
-
<cide-ele-icon class="tw-text-red-500">error</cide-ele-icon>
|
|
7315
|
-
<span>{{ getFormGroupErrorMessage() }}</span>
|
|
7316
|
-
</div>
|
|
7317
|
-
}
|
|
7318
|
-
} @else {
|
|
7319
|
-
<!-- Individual Field Error Display -->
|
|
7320
|
-
@if (control && control.invalid && (control.dirty || control.touched)) {
|
|
7321
|
-
<div class="tw-text-red-500 tw-text-xs tw-mt-1 tw-flex tw-items-center tw-gap-1">
|
|
7322
|
-
<cide-ele-icon class="tw-text-xs">error</cide-ele-icon>
|
|
7323
|
-
<span>{{ getErrorMessage() }}</span>
|
|
7324
|
-
</div>
|
|
7325
|
-
}
|
|
7326
|
-
}
|
|
7747
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideFormFieldErrorComponent, isStandalone: true, selector: "cide-form-field-error", inputs: { control: "control", formGroup: "formGroup", fieldName: "fieldName", customMessages: "customMessages" }, usesOnChanges: true, ngImport: i0, template: `
|
|
7748
|
+
@if (isFormGroupMode()) {
|
|
7749
|
+
<!-- Form Group Error Display -->
|
|
7750
|
+
@if (formErrors().length > 0) {
|
|
7751
|
+
<div class="tw-flex tw-items-center tw-gap-2 tw-text-red-600 tw-text-sm">
|
|
7752
|
+
<cide-ele-icon class="tw-text-red-500">error</cide-ele-icon>
|
|
7753
|
+
<span>{{ getFormGroupErrorMessage() }}</span>
|
|
7754
|
+
</div>
|
|
7755
|
+
}
|
|
7756
|
+
} @else {
|
|
7757
|
+
<!-- Individual Field Error Display -->
|
|
7758
|
+
@if (control && control.invalid && (control.dirty || control.touched)) {
|
|
7759
|
+
<div class="tw-text-red-500 tw-text-xs tw-mt-1 tw-flex tw-items-center tw-gap-1">
|
|
7760
|
+
<cide-ele-icon class="tw-text-xs">error</cide-ele-icon>
|
|
7761
|
+
<span>{{ getErrorMessage() }}</span>
|
|
7762
|
+
</div>
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7327
7765
|
`, isInline: true, dependencies: [{ kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
7328
7766
|
}
|
|
7329
7767
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideFormFieldErrorComponent, decorators: [{
|
|
@@ -7331,24 +7769,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
7331
7769
|
args: [{
|
|
7332
7770
|
selector: 'cide-form-field-error',
|
|
7333
7771
|
standalone: true,
|
|
7334
|
-
template: `
|
|
7335
|
-
@if (isFormGroupMode()) {
|
|
7336
|
-
<!-- Form Group Error Display -->
|
|
7337
|
-
@if (formErrors().length > 0) {
|
|
7338
|
-
<div class="tw-flex tw-items-center tw-gap-2 tw-text-red-600 tw-text-sm">
|
|
7339
|
-
<cide-ele-icon class="tw-text-red-500">error</cide-ele-icon>
|
|
7340
|
-
<span>{{ getFormGroupErrorMessage() }}</span>
|
|
7341
|
-
</div>
|
|
7342
|
-
}
|
|
7343
|
-
} @else {
|
|
7344
|
-
<!-- Individual Field Error Display -->
|
|
7345
|
-
@if (control && control.invalid && (control.dirty || control.touched)) {
|
|
7346
|
-
<div class="tw-text-red-500 tw-text-xs tw-mt-1 tw-flex tw-items-center tw-gap-1">
|
|
7347
|
-
<cide-ele-icon class="tw-text-xs">error</cide-ele-icon>
|
|
7348
|
-
<span>{{ getErrorMessage() }}</span>
|
|
7349
|
-
</div>
|
|
7350
|
-
}
|
|
7351
|
-
}
|
|
7772
|
+
template: `
|
|
7773
|
+
@if (isFormGroupMode()) {
|
|
7774
|
+
<!-- Form Group Error Display -->
|
|
7775
|
+
@if (formErrors().length > 0) {
|
|
7776
|
+
<div class="tw-flex tw-items-center tw-gap-2 tw-text-red-600 tw-text-sm">
|
|
7777
|
+
<cide-ele-icon class="tw-text-red-500">error</cide-ele-icon>
|
|
7778
|
+
<span>{{ getFormGroupErrorMessage() }}</span>
|
|
7779
|
+
</div>
|
|
7780
|
+
}
|
|
7781
|
+
} @else {
|
|
7782
|
+
<!-- Individual Field Error Display -->
|
|
7783
|
+
@if (control && control.invalid && (control.dirty || control.touched)) {
|
|
7784
|
+
<div class="tw-text-red-500 tw-text-xs tw-mt-1 tw-flex tw-items-center tw-gap-1">
|
|
7785
|
+
<cide-ele-icon class="tw-text-xs">error</cide-ele-icon>
|
|
7786
|
+
<span>{{ getErrorMessage() }}</span>
|
|
7787
|
+
</div>
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7352
7790
|
`,
|
|
7353
7791
|
imports: [CideIconComponent]
|
|
7354
7792
|
}]
|
|
@@ -7568,146 +8006,168 @@ class CideEleToastNotificationComponent {
|
|
|
7568
8006
|
return notification.id;
|
|
7569
8007
|
}
|
|
7570
8008
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleToastNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7571
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleToastNotificationComponent, isStandalone: true, selector: "cide-ele-toast-notification", ngImport: i0, template: `
|
|
7572
|
-
<!-- Toast Container -->
|
|
7573
|
-
<div class="toast-container">
|
|
7574
|
-
<div
|
|
7575
|
-
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
7576
|
-
class="toast-notification"
|
|
7577
|
-
[class]="getToastClass(notification)"
|
|
7578
|
-
[@toastAnimation]>
|
|
7579
|
-
|
|
7580
|
-
<!-- Toast Content -->
|
|
7581
|
-
<div class="toast-content">
|
|
7582
|
-
<!-- Icon -->
|
|
7583
|
-
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
7584
|
-
<cide-ele-icon size="sm">
|
|
7585
|
-
{{ notification.icon }}
|
|
7586
|
-
</cide-ele-icon>
|
|
7587
|
-
</div>
|
|
7588
|
-
|
|
7589
|
-
<!-- Text Content -->
|
|
7590
|
-
<div class="toast-text">
|
|
7591
|
-
<div *ngIf="notification.title" class="toast-title">
|
|
7592
|
-
{{ notification.title }}
|
|
7593
|
-
</div>
|
|
7594
|
-
<div class="toast-message">
|
|
7595
|
-
{{ notification.message }}
|
|
7596
|
-
</div>
|
|
7597
|
-
</div>
|
|
7598
|
-
|
|
7599
|
-
<!-- Close Button -->
|
|
7600
|
-
<button
|
|
7601
|
-
type="button"
|
|
7602
|
-
class="toast-close"
|
|
7603
|
-
(click)="removeNotification(notification.id)"
|
|
7604
|
-
aria-label="Close notification">
|
|
7605
|
-
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
7606
|
-
</button>
|
|
7607
|
-
</div>
|
|
7608
|
-
|
|
7609
|
-
<!-- Undo Button -->
|
|
7610
|
-
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
7611
|
-
<button
|
|
7612
|
-
type="button"
|
|
7613
|
-
class="toast-undo-btn"
|
|
7614
|
-
(click)="executeUndo(notification.id)">
|
|
7615
|
-
{{ notification.undoText }}
|
|
7616
|
-
</button>
|
|
7617
|
-
</div>
|
|
7618
|
-
|
|
7619
|
-
<!-- Progress Bar -->
|
|
7620
|
-
<div
|
|
7621
|
-
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
7622
|
-
class="toast-progress"
|
|
7623
|
-
[style.animation-duration]="notification.duration + 'ms'">
|
|
7624
|
-
</div>
|
|
7625
|
-
|
|
7626
|
-
<!-- Custom Progress Bar -->
|
|
7627
|
-
<div
|
|
7628
|
-
*ngIf="notification.showProgress"
|
|
7629
|
-
class="toast-custom-progress">
|
|
7630
|
-
<div
|
|
7631
|
-
class="toast-custom-progress-bar"
|
|
7632
|
-
[style.width.%]="notification.progress || 0">
|
|
7633
|
-
</div>
|
|
7634
|
-
</div>
|
|
7635
|
-
</div>
|
|
7636
|
-
</div>
|
|
8009
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleToastNotificationComponent, isStandalone: true, selector: "cide-ele-toast-notification", ngImport: i0, template: `
|
|
8010
|
+
<!-- Toast Container -->
|
|
8011
|
+
<div class="toast-container">
|
|
8012
|
+
<div
|
|
8013
|
+
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
8014
|
+
class="toast-notification"
|
|
8015
|
+
[class]="getToastClass(notification)"
|
|
8016
|
+
[@toastAnimation]>
|
|
8017
|
+
|
|
8018
|
+
<!-- Toast Content -->
|
|
8019
|
+
<div class="toast-content">
|
|
8020
|
+
<!-- Icon -->
|
|
8021
|
+
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
8022
|
+
<cide-ele-icon size="sm">
|
|
8023
|
+
{{ notification.icon }}
|
|
8024
|
+
</cide-ele-icon>
|
|
8025
|
+
</div>
|
|
8026
|
+
|
|
8027
|
+
<!-- Text Content -->
|
|
8028
|
+
<div class="toast-text">
|
|
8029
|
+
<div *ngIf="notification.title" class="toast-title">
|
|
8030
|
+
{{ notification.title }}
|
|
8031
|
+
</div>
|
|
8032
|
+
<div class="toast-message">
|
|
8033
|
+
{{ notification.message }}
|
|
8034
|
+
</div>
|
|
8035
|
+
</div>
|
|
8036
|
+
|
|
8037
|
+
<!-- Close Button -->
|
|
8038
|
+
<button
|
|
8039
|
+
type="button"
|
|
8040
|
+
class="toast-close"
|
|
8041
|
+
(click)="removeNotification(notification.id)"
|
|
8042
|
+
aria-label="Close notification">
|
|
8043
|
+
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
8044
|
+
</button>
|
|
8045
|
+
</div>
|
|
8046
|
+
|
|
8047
|
+
<!-- Undo Button -->
|
|
8048
|
+
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
8049
|
+
<button
|
|
8050
|
+
type="button"
|
|
8051
|
+
class="toast-undo-btn"
|
|
8052
|
+
(click)="executeUndo(notification.id)">
|
|
8053
|
+
{{ notification.undoText }}
|
|
8054
|
+
</button>
|
|
8055
|
+
</div>
|
|
8056
|
+
|
|
8057
|
+
<!-- Progress Bar -->
|
|
8058
|
+
<div
|
|
8059
|
+
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
8060
|
+
class="toast-progress"
|
|
8061
|
+
[style.animation-duration]="notification.duration + 'ms'">
|
|
8062
|
+
</div>
|
|
8063
|
+
|
|
8064
|
+
<!-- Custom Progress Bar -->
|
|
8065
|
+
<div
|
|
8066
|
+
*ngIf="notification.showProgress"
|
|
8067
|
+
class="toast-custom-progress">
|
|
8068
|
+
<div
|
|
8069
|
+
class="toast-custom-progress-bar"
|
|
8070
|
+
[style.width.%]="notification.progress || 0">
|
|
8071
|
+
</div>
|
|
8072
|
+
</div>
|
|
8073
|
+
</div>
|
|
8074
|
+
</div>
|
|
7637
8075
|
`, isInline: true, styles: [".toast-container{position:fixed;top:80px;right:20px;z-index:9990;display:flex;flex-direction:column;gap:12px;max-width:400px;width:100%}.toast-notification{background:#fff;border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;border:1px solid #e5e7eb;overflow:hidden;position:relative;animation:slideInRight .3s ease-out}.toast-notification.success{border-left:4px solid #16a34a}.toast-notification.error{border-left:4px solid #dc2626}.toast-notification.warning{border-left:4px solid #d97706}.toast-notification.info{border-left:4px solid #2563eb}.toast-content{display:flex;align-items:flex-start;gap:12px;padding:16px}.toast-icon{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px}.toast-icon.success{background-color:#f0fdf4;color:#16a34a}.toast-icon.error{background-color:#fef2f2;color:#dc2626}.toast-icon.warning{background-color:#fffbeb;color:#d97706}.toast-icon.info{background-color:#eff6ff;color:#2563eb}.toast-text{flex:1;min-width:0}.toast-title{font-size:14px;font-weight:600;color:#111827;margin-bottom:4px;line-height:1.4}.toast-message{font-size:14px;color:#6b7280;line-height:1.4}.toast-close{background:none;border:none;color:#9ca3af;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;transition:all .2s ease;flex-shrink:0}.toast-close:hover{background-color:#f3f4f6;color:#6b7280}.toast-actions{padding:0 16px 16px;display:flex;justify-content:flex-end}.toast-undo-btn{background:none;border:none;color:#2563eb;font-size:14px;font-weight:500;cursor:pointer;padding:8px 12px;border-radius:6px;transition:all .2s ease}.toast-undo-btn:hover{background-color:#eff6ff}.toast-progress{position:absolute;bottom:0;left:0;height:3px;background-color:#e5e7eb;animation:progressShrink linear forwards}.toast-notification.success .toast-progress{background-color:#16a34a}.toast-notification.error .toast-progress{background-color:#dc2626}.toast-notification.warning .toast-progress{background-color:#d97706}.toast-notification.info .toast-progress{background-color:#2563eb}.toast-custom-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background-color:#e5e7eb;overflow:hidden}.toast-custom-progress-bar{height:100%;background-color:#2563eb;transition:width .3s ease;border-radius:0 3px 0 0}.toast-notification.success .toast-custom-progress-bar{background-color:#16a34a}.toast-notification.error .toast-custom-progress-bar{background-color:#dc2626}.toast-notification.warning .toast-custom-progress-bar{background-color:#d97706}.toast-notification.info .toast-custom-progress-bar{background-color:#2563eb}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideOutRight{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}@keyframes progressShrink{0%{width:100%}to{width:0%}}@media (max-width: 640px){.toast-container{top:80px;right:10px;left:10px;max-width:none}.toast-notification{border-radius:6px}.toast-content{padding:12px}.toast-actions{padding:0 12px 12px}}.toast-close:focus,.toast-undo-btn:focus{outline:2px solid #2563eb;outline-offset:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }], animations: [
|
|
7638
|
-
|
|
8076
|
+
trigger('toastAnimation', [
|
|
8077
|
+
state('void', style({
|
|
8078
|
+
transform: 'translateX(100%)',
|
|
8079
|
+
opacity: 0
|
|
8080
|
+
})),
|
|
8081
|
+
state('*', style({
|
|
8082
|
+
transform: 'translateX(0)',
|
|
8083
|
+
opacity: 1
|
|
8084
|
+
})),
|
|
8085
|
+
transition('void => *', animate('300ms ease-out')),
|
|
8086
|
+
transition('* => void', animate('200ms ease-in'))
|
|
8087
|
+
])
|
|
7639
8088
|
] });
|
|
7640
8089
|
}
|
|
7641
8090
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleToastNotificationComponent, decorators: [{
|
|
7642
8091
|
type: Component,
|
|
7643
|
-
args: [{ selector: 'cide-ele-toast-notification', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
7644
|
-
<!-- Toast Container -->
|
|
7645
|
-
<div class="toast-container">
|
|
7646
|
-
<div
|
|
7647
|
-
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
7648
|
-
class="toast-notification"
|
|
7649
|
-
[class]="getToastClass(notification)"
|
|
7650
|
-
[@toastAnimation]>
|
|
7651
|
-
|
|
7652
|
-
<!-- Toast Content -->
|
|
7653
|
-
<div class="toast-content">
|
|
7654
|
-
<!-- Icon -->
|
|
7655
|
-
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
7656
|
-
<cide-ele-icon size="sm">
|
|
7657
|
-
{{ notification.icon }}
|
|
7658
|
-
</cide-ele-icon>
|
|
7659
|
-
</div>
|
|
7660
|
-
|
|
7661
|
-
<!-- Text Content -->
|
|
7662
|
-
<div class="toast-text">
|
|
7663
|
-
<div *ngIf="notification.title" class="toast-title">
|
|
7664
|
-
{{ notification.title }}
|
|
7665
|
-
</div>
|
|
7666
|
-
<div class="toast-message">
|
|
7667
|
-
{{ notification.message }}
|
|
7668
|
-
</div>
|
|
7669
|
-
</div>
|
|
7670
|
-
|
|
7671
|
-
<!-- Close Button -->
|
|
7672
|
-
<button
|
|
7673
|
-
type="button"
|
|
7674
|
-
class="toast-close"
|
|
7675
|
-
(click)="removeNotification(notification.id)"
|
|
7676
|
-
aria-label="Close notification">
|
|
7677
|
-
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
7678
|
-
</button>
|
|
7679
|
-
</div>
|
|
7680
|
-
|
|
7681
|
-
<!-- Undo Button -->
|
|
7682
|
-
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
7683
|
-
<button
|
|
7684
|
-
type="button"
|
|
7685
|
-
class="toast-undo-btn"
|
|
7686
|
-
(click)="executeUndo(notification.id)">
|
|
7687
|
-
{{ notification.undoText }}
|
|
7688
|
-
</button>
|
|
7689
|
-
</div>
|
|
7690
|
-
|
|
7691
|
-
<!-- Progress Bar -->
|
|
7692
|
-
<div
|
|
7693
|
-
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
7694
|
-
class="toast-progress"
|
|
7695
|
-
[style.animation-duration]="notification.duration + 'ms'">
|
|
7696
|
-
</div>
|
|
7697
|
-
|
|
7698
|
-
<!-- Custom Progress Bar -->
|
|
7699
|
-
<div
|
|
7700
|
-
*ngIf="notification.showProgress"
|
|
7701
|
-
class="toast-custom-progress">
|
|
7702
|
-
<div
|
|
7703
|
-
class="toast-custom-progress-bar"
|
|
7704
|
-
[style.width.%]="notification.progress || 0">
|
|
7705
|
-
</div>
|
|
7706
|
-
</div>
|
|
7707
|
-
</div>
|
|
7708
|
-
</div>
|
|
8092
|
+
args: [{ selector: 'cide-ele-toast-notification', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
8093
|
+
<!-- Toast Container -->
|
|
8094
|
+
<div class="toast-container">
|
|
8095
|
+
<div
|
|
8096
|
+
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
8097
|
+
class="toast-notification"
|
|
8098
|
+
[class]="getToastClass(notification)"
|
|
8099
|
+
[@toastAnimation]>
|
|
8100
|
+
|
|
8101
|
+
<!-- Toast Content -->
|
|
8102
|
+
<div class="toast-content">
|
|
8103
|
+
<!-- Icon -->
|
|
8104
|
+
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
8105
|
+
<cide-ele-icon size="sm">
|
|
8106
|
+
{{ notification.icon }}
|
|
8107
|
+
</cide-ele-icon>
|
|
8108
|
+
</div>
|
|
8109
|
+
|
|
8110
|
+
<!-- Text Content -->
|
|
8111
|
+
<div class="toast-text">
|
|
8112
|
+
<div *ngIf="notification.title" class="toast-title">
|
|
8113
|
+
{{ notification.title }}
|
|
8114
|
+
</div>
|
|
8115
|
+
<div class="toast-message">
|
|
8116
|
+
{{ notification.message }}
|
|
8117
|
+
</div>
|
|
8118
|
+
</div>
|
|
8119
|
+
|
|
8120
|
+
<!-- Close Button -->
|
|
8121
|
+
<button
|
|
8122
|
+
type="button"
|
|
8123
|
+
class="toast-close"
|
|
8124
|
+
(click)="removeNotification(notification.id)"
|
|
8125
|
+
aria-label="Close notification">
|
|
8126
|
+
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
8127
|
+
</button>
|
|
8128
|
+
</div>
|
|
8129
|
+
|
|
8130
|
+
<!-- Undo Button -->
|
|
8131
|
+
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
8132
|
+
<button
|
|
8133
|
+
type="button"
|
|
8134
|
+
class="toast-undo-btn"
|
|
8135
|
+
(click)="executeUndo(notification.id)">
|
|
8136
|
+
{{ notification.undoText }}
|
|
8137
|
+
</button>
|
|
8138
|
+
</div>
|
|
8139
|
+
|
|
8140
|
+
<!-- Progress Bar -->
|
|
8141
|
+
<div
|
|
8142
|
+
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
8143
|
+
class="toast-progress"
|
|
8144
|
+
[style.animation-duration]="notification.duration + 'ms'">
|
|
8145
|
+
</div>
|
|
8146
|
+
|
|
8147
|
+
<!-- Custom Progress Bar -->
|
|
8148
|
+
<div
|
|
8149
|
+
*ngIf="notification.showProgress"
|
|
8150
|
+
class="toast-custom-progress">
|
|
8151
|
+
<div
|
|
8152
|
+
class="toast-custom-progress-bar"
|
|
8153
|
+
[style.width.%]="notification.progress || 0">
|
|
8154
|
+
</div>
|
|
8155
|
+
</div>
|
|
8156
|
+
</div>
|
|
8157
|
+
</div>
|
|
7709
8158
|
`, animations: [
|
|
7710
|
-
|
|
8159
|
+
trigger('toastAnimation', [
|
|
8160
|
+
state('void', style({
|
|
8161
|
+
transform: 'translateX(100%)',
|
|
8162
|
+
opacity: 0
|
|
8163
|
+
})),
|
|
8164
|
+
state('*', style({
|
|
8165
|
+
transform: 'translateX(0)',
|
|
8166
|
+
opacity: 1
|
|
8167
|
+
})),
|
|
8168
|
+
transition('void => *', animate('300ms ease-out')),
|
|
8169
|
+
transition('* => void', animate('200ms ease-in'))
|
|
8170
|
+
])
|
|
7711
8171
|
], styles: [".toast-container{position:fixed;top:80px;right:20px;z-index:9990;display:flex;flex-direction:column;gap:12px;max-width:400px;width:100%}.toast-notification{background:#fff;border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;border:1px solid #e5e7eb;overflow:hidden;position:relative;animation:slideInRight .3s ease-out}.toast-notification.success{border-left:4px solid #16a34a}.toast-notification.error{border-left:4px solid #dc2626}.toast-notification.warning{border-left:4px solid #d97706}.toast-notification.info{border-left:4px solid #2563eb}.toast-content{display:flex;align-items:flex-start;gap:12px;padding:16px}.toast-icon{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px}.toast-icon.success{background-color:#f0fdf4;color:#16a34a}.toast-icon.error{background-color:#fef2f2;color:#dc2626}.toast-icon.warning{background-color:#fffbeb;color:#d97706}.toast-icon.info{background-color:#eff6ff;color:#2563eb}.toast-text{flex:1;min-width:0}.toast-title{font-size:14px;font-weight:600;color:#111827;margin-bottom:4px;line-height:1.4}.toast-message{font-size:14px;color:#6b7280;line-height:1.4}.toast-close{background:none;border:none;color:#9ca3af;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;transition:all .2s ease;flex-shrink:0}.toast-close:hover{background-color:#f3f4f6;color:#6b7280}.toast-actions{padding:0 16px 16px;display:flex;justify-content:flex-end}.toast-undo-btn{background:none;border:none;color:#2563eb;font-size:14px;font-weight:500;cursor:pointer;padding:8px 12px;border-radius:6px;transition:all .2s ease}.toast-undo-btn:hover{background-color:#eff6ff}.toast-progress{position:absolute;bottom:0;left:0;height:3px;background-color:#e5e7eb;animation:progressShrink linear forwards}.toast-notification.success .toast-progress{background-color:#16a34a}.toast-notification.error .toast-progress{background-color:#dc2626}.toast-notification.warning .toast-progress{background-color:#d97706}.toast-notification.info .toast-progress{background-color:#2563eb}.toast-custom-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background-color:#e5e7eb;overflow:hidden}.toast-custom-progress-bar{height:100%;background-color:#2563eb;transition:width .3s ease;border-radius:0 3px 0 0}.toast-notification.success .toast-custom-progress-bar{background-color:#16a34a}.toast-notification.error .toast-custom-progress-bar{background-color:#dc2626}.toast-notification.warning .toast-custom-progress-bar{background-color:#d97706}.toast-notification.info .toast-custom-progress-bar{background-color:#2563eb}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideOutRight{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}@keyframes progressShrink{0%{width:100%}to{width:0%}}@media (max-width: 640px){.toast-container{top:80px;right:10px;left:10px;max-width:none}.toast-notification{border-radius:6px}.toast-content{padding:12px}.toast-actions{padding:0 12px 12px}}.toast-close:focus,.toast-undo-btn:focus{outline:2px solid #2563eb;outline-offset:2px}\n"] }]
|
|
7712
8172
|
}] });
|
|
7713
8173
|
|
|
@@ -7766,6 +8226,17 @@ const DEFAULT_GRID_CONFIG = {
|
|
|
7766
8226
|
virtualScroll: false,
|
|
7767
8227
|
rowHeight: 40
|
|
7768
8228
|
},
|
|
8229
|
+
columnMenu: {
|
|
8230
|
+
enabled: true,
|
|
8231
|
+
showSort: true,
|
|
8232
|
+
showFilter: true,
|
|
8233
|
+
showAutosize: true,
|
|
8234
|
+
showGroupBy: true,
|
|
8235
|
+
showManageColumns: true,
|
|
8236
|
+
showResetColumns: true,
|
|
8237
|
+
showHideColumn: true,
|
|
8238
|
+
showAggregation: true
|
|
8239
|
+
},
|
|
7769
8240
|
responsive: true,
|
|
7770
8241
|
striped: false,
|
|
7771
8242
|
bordered: true,
|
|
@@ -7806,20 +8277,46 @@ class CideEleDataGridComponent {
|
|
|
7806
8277
|
localReorderedData = []; // Local data for visual reordering
|
|
7807
8278
|
searchDebounceTimer;
|
|
7808
8279
|
isDataUpdate = false;
|
|
8280
|
+
// Column menu state
|
|
8281
|
+
openColumnMenu = signal(null, ...(ngDevMode ? [{ debugName: "openColumnMenu" }] : [])); // Column key of open menu
|
|
8282
|
+
columnFilters = signal([], ...(ngDevMode ? [{ debugName: "columnFilters" }] : [])); // Active column filters
|
|
8283
|
+
columnSorts = signal([], ...(ngDevMode ? [{ debugName: "columnSorts" }] : [])); // Active column sorts
|
|
8284
|
+
hiddenColumns = signal([], ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : [])); // Hidden column keys
|
|
8285
|
+
showFilterPanel = signal(null, ...(ngDevMode ? [{ debugName: "showFilterPanel" }] : [])); // Column key for active filter panel
|
|
8286
|
+
filterSearchTerm = signal('', ...(ngDevMode ? [{ debugName: "filterSearchTerm" }] : [])); // Search term for filter values
|
|
7809
8287
|
// Computed properties
|
|
7810
8288
|
hasNextPage = computed(() => this.currentPage() < this.totalPages(), ...(ngDevMode ? [{ debugName: "hasNextPage" }] : []));
|
|
7811
8289
|
hasPreviousPage = computed(() => this.currentPage() > 1, ...(ngDevMode ? [{ debugName: "hasPreviousPage" }] : []));
|
|
7812
8290
|
// Merged configuration with defaults
|
|
7813
8291
|
mergedConfig = signal(this.config, ...(ngDevMode ? [{ debugName: "mergedConfig" }] : []));
|
|
8292
|
+
// Visible columns (excluding hidden)
|
|
8293
|
+
visibleColumns = computed(() => {
|
|
8294
|
+
const hidden = this.hiddenColumns();
|
|
8295
|
+
return this.mergedConfig().columns.filter(col => !hidden.includes(col.key) && !col.hidden);
|
|
8296
|
+
}, ...(ngDevMode ? [{ debugName: "visibleColumns" }] : []));
|
|
7814
8297
|
ngOnInit() {
|
|
7815
8298
|
this.initializeGrid();
|
|
7816
8299
|
this.initializeOriginalOrder();
|
|
8300
|
+
// Add global click listener to close menus when clicking outside
|
|
8301
|
+
document.addEventListener('click', this.handleGlobalClick.bind(this));
|
|
7817
8302
|
}
|
|
7818
8303
|
ngOnDestroy() {
|
|
7819
8304
|
// Clean up search debounce timer
|
|
7820
8305
|
if (this.searchDebounceTimer) {
|
|
7821
8306
|
clearTimeout(this.searchDebounceTimer);
|
|
7822
8307
|
}
|
|
8308
|
+
// Remove global click listener
|
|
8309
|
+
document.removeEventListener('click', this.handleGlobalClick.bind(this));
|
|
8310
|
+
}
|
|
8311
|
+
handleGlobalClick(event) {
|
|
8312
|
+
// Close column menu and filter panel when clicking outside
|
|
8313
|
+
const target = event.target;
|
|
8314
|
+
const isInsideMenu = target.closest('.column-menu-dropdown');
|
|
8315
|
+
const isMenuTrigger = target.closest('.column-menu-trigger');
|
|
8316
|
+
if (!isInsideMenu && !isMenuTrigger) {
|
|
8317
|
+
this.closeColumnMenu();
|
|
8318
|
+
this.closeFilterPanel();
|
|
8319
|
+
}
|
|
7823
8320
|
}
|
|
7824
8321
|
ngOnChanges(changes) {
|
|
7825
8322
|
if (changes['config']) {
|
|
@@ -9049,9 +9546,287 @@ class CideEleDataGridComponent {
|
|
|
9049
9546
|
}
|
|
9050
9547
|
this.emitEvent('action', item, undefined, action);
|
|
9051
9548
|
}
|
|
9549
|
+
// ===== Column Menu Methods =====
|
|
9550
|
+
toggleColumnMenu(columnKey, event) {
|
|
9551
|
+
if (event) {
|
|
9552
|
+
event.stopPropagation();
|
|
9553
|
+
}
|
|
9554
|
+
const current = this.openColumnMenu();
|
|
9555
|
+
this.openColumnMenu.set(current === columnKey ? null : columnKey);
|
|
9556
|
+
}
|
|
9557
|
+
closeColumnMenu() {
|
|
9558
|
+
this.openColumnMenu.set(null);
|
|
9559
|
+
}
|
|
9560
|
+
isColumnMenuOpen(columnKey) {
|
|
9561
|
+
return this.openColumnMenu() === columnKey;
|
|
9562
|
+
}
|
|
9563
|
+
onColumnSort(column, direction) {
|
|
9564
|
+
const sorts = this.columnSorts();
|
|
9565
|
+
const existingIndex = sorts.findIndex(s => s.columnKey === column.key);
|
|
9566
|
+
if (existingIndex !== -1) {
|
|
9567
|
+
// Update existing sort
|
|
9568
|
+
sorts[existingIndex].direction = direction;
|
|
9569
|
+
}
|
|
9570
|
+
else {
|
|
9571
|
+
// Add new sort (replace existing for single column sort, or add for multi-column)
|
|
9572
|
+
sorts.push({ columnKey: column.key, direction });
|
|
9573
|
+
}
|
|
9574
|
+
this.columnSorts.set([...sorts]);
|
|
9575
|
+
this.emitEvent('columnSort', { columnKey: column.key, direction }, column);
|
|
9576
|
+
this.closeColumnMenu();
|
|
9577
|
+
}
|
|
9578
|
+
onColumnFilter(column, value, operator = 'equals') {
|
|
9579
|
+
const filters = this.columnFilters();
|
|
9580
|
+
const existingIndex = filters.findIndex(f => f.columnKey === column.key);
|
|
9581
|
+
if (value === null || value === undefined || value === '') {
|
|
9582
|
+
// Remove filter
|
|
9583
|
+
if (existingIndex !== -1) {
|
|
9584
|
+
filters.splice(existingIndex, 1);
|
|
9585
|
+
}
|
|
9586
|
+
}
|
|
9587
|
+
else {
|
|
9588
|
+
const filter = { columnKey: column.key, value, operator };
|
|
9589
|
+
if (existingIndex !== -1) {
|
|
9590
|
+
// Update existing filter
|
|
9591
|
+
filters[existingIndex] = filter;
|
|
9592
|
+
}
|
|
9593
|
+
else {
|
|
9594
|
+
// Add new filter
|
|
9595
|
+
filters.push(filter);
|
|
9596
|
+
}
|
|
9597
|
+
}
|
|
9598
|
+
this.columnFilters.set([...filters]);
|
|
9599
|
+
this.emitEvent('columnFilter', filters, column);
|
|
9600
|
+
}
|
|
9601
|
+
onColumnAutosize(column) {
|
|
9602
|
+
this.emitEvent('columnAutosize', column.key, column);
|
|
9603
|
+
this.closeColumnMenu();
|
|
9604
|
+
}
|
|
9605
|
+
onColumnGroupBy(column) {
|
|
9606
|
+
this.emitEvent('columnGroupBy', column.key, column);
|
|
9607
|
+
this.closeColumnMenu();
|
|
9608
|
+
}
|
|
9609
|
+
onColumnHide(column) {
|
|
9610
|
+
const hidden = this.hiddenColumns();
|
|
9611
|
+
if (!hidden.includes(column.key)) {
|
|
9612
|
+
this.hiddenColumns.set([...hidden, column.key]);
|
|
9613
|
+
this.emitEvent('columnHide', column.key, column);
|
|
9614
|
+
}
|
|
9615
|
+
this.closeColumnMenu();
|
|
9616
|
+
}
|
|
9617
|
+
onColumnShow(columnKey) {
|
|
9618
|
+
const hidden = this.hiddenColumns();
|
|
9619
|
+
const index = hidden.indexOf(columnKey);
|
|
9620
|
+
if (index !== -1) {
|
|
9621
|
+
hidden.splice(index, 1);
|
|
9622
|
+
this.hiddenColumns.set([...hidden]);
|
|
9623
|
+
const column = this.mergedConfig().columns.find(c => c.key === columnKey);
|
|
9624
|
+
if (column) {
|
|
9625
|
+
this.emitEvent('columnShow', columnKey, column);
|
|
9626
|
+
}
|
|
9627
|
+
}
|
|
9628
|
+
}
|
|
9629
|
+
onColumnReset() {
|
|
9630
|
+
this.hiddenColumns.set([]);
|
|
9631
|
+
this.columnFilters.set([]);
|
|
9632
|
+
this.columnSorts.set([]);
|
|
9633
|
+
this.emitEvent('columnReset', null);
|
|
9634
|
+
this.closeColumnMenu();
|
|
9635
|
+
}
|
|
9636
|
+
onColumnAggregation(column, aggregationType) {
|
|
9637
|
+
this.emitEvent('columnAggregation', { columnKey: column.key, aggregationType }, column);
|
|
9638
|
+
this.closeColumnMenu();
|
|
9639
|
+
}
|
|
9640
|
+
onManageColumns() {
|
|
9641
|
+
this.emitEvent('manageColumns', null);
|
|
9642
|
+
this.closeColumnMenu();
|
|
9643
|
+
}
|
|
9644
|
+
getColumnFilterValue(columnKey) {
|
|
9645
|
+
const filter = this.columnFilters().find(f => f.columnKey === columnKey);
|
|
9646
|
+
return filter?.value;
|
|
9647
|
+
}
|
|
9648
|
+
getColumnSortDirection(columnKey) {
|
|
9649
|
+
const sort = this.columnSorts().find(s => s.columnKey === columnKey);
|
|
9650
|
+
return sort?.direction || null;
|
|
9651
|
+
}
|
|
9652
|
+
isColumnFiltered(columnKey) {
|
|
9653
|
+
return this.columnFilters().some(f => f.columnKey === columnKey);
|
|
9654
|
+
}
|
|
9655
|
+
getActiveFilterCount(columnKey) {
|
|
9656
|
+
const filter = this.columnFilters().find(f => f.columnKey === columnKey);
|
|
9657
|
+
if (filter && filter.operator === 'in' && Array.isArray(filter.value)) {
|
|
9658
|
+
return filter.value.length;
|
|
9659
|
+
}
|
|
9660
|
+
return 0;
|
|
9661
|
+
}
|
|
9662
|
+
toggleFilterPanel(columnKey, event) {
|
|
9663
|
+
if (event) {
|
|
9664
|
+
event.stopPropagation();
|
|
9665
|
+
}
|
|
9666
|
+
const current = this.showFilterPanel();
|
|
9667
|
+
this.showFilterPanel.set(current === columnKey ? null : columnKey);
|
|
9668
|
+
this.filterSearchTerm.set('');
|
|
9669
|
+
}
|
|
9670
|
+
closeFilterPanel() {
|
|
9671
|
+
this.showFilterPanel.set(null);
|
|
9672
|
+
this.filterSearchTerm.set('');
|
|
9673
|
+
}
|
|
9674
|
+
isFilterPanelOpen(columnKey) {
|
|
9675
|
+
return this.showFilterPanel() === columnKey;
|
|
9676
|
+
}
|
|
9677
|
+
getUniqueColumnValues(column) {
|
|
9678
|
+
const data = this.internalData();
|
|
9679
|
+
const values = new Map();
|
|
9680
|
+
// Count occurrences of each value
|
|
9681
|
+
data.forEach(row => {
|
|
9682
|
+
const value = column.valueGetter
|
|
9683
|
+
? this.getNestedValue(row, column.valueGetter)
|
|
9684
|
+
: row[column.key];
|
|
9685
|
+
const count = values.get(value) || 0;
|
|
9686
|
+
values.set(value, count + 1);
|
|
9687
|
+
});
|
|
9688
|
+
// Get active filter for this column
|
|
9689
|
+
const activeFilter = this.columnFilters().find(f => f.columnKey === column.key);
|
|
9690
|
+
const activeValues = activeFilter?.operator === 'in' && Array.isArray(activeFilter.value)
|
|
9691
|
+
? activeFilter.value
|
|
9692
|
+
: [];
|
|
9693
|
+
// Convert to array and sort
|
|
9694
|
+
const result = Array.from(values.entries())
|
|
9695
|
+
.map(([value, count]) => ({
|
|
9696
|
+
value,
|
|
9697
|
+
label: this.formatFilterValue(value, column),
|
|
9698
|
+
count,
|
|
9699
|
+
checked: activeValues.length === 0 || activeValues.includes(value)
|
|
9700
|
+
}))
|
|
9701
|
+
.sort((a, b) => {
|
|
9702
|
+
// Sort by label
|
|
9703
|
+
if (a.label < b.label)
|
|
9704
|
+
return -1;
|
|
9705
|
+
if (a.label > b.label)
|
|
9706
|
+
return 1;
|
|
9707
|
+
return 0;
|
|
9708
|
+
});
|
|
9709
|
+
// Filter by search term
|
|
9710
|
+
const searchTerm = this.filterSearchTerm().toLowerCase();
|
|
9711
|
+
if (searchTerm) {
|
|
9712
|
+
return result.filter(item => item.label.toLowerCase().includes(searchTerm));
|
|
9713
|
+
}
|
|
9714
|
+
return result;
|
|
9715
|
+
}
|
|
9716
|
+
formatFilterValue(value, column) {
|
|
9717
|
+
if (value === null || value === undefined)
|
|
9718
|
+
return '(Blank)';
|
|
9719
|
+
if (typeof value === 'boolean')
|
|
9720
|
+
return value ? 'Yes' : 'No';
|
|
9721
|
+
if (value instanceof Date)
|
|
9722
|
+
return value.toLocaleDateString();
|
|
9723
|
+
return String(value);
|
|
9724
|
+
}
|
|
9725
|
+
toggleFilterValue(column, value, checked) {
|
|
9726
|
+
const filters = this.columnFilters();
|
|
9727
|
+
const existingIndex = filters.findIndex(f => f.columnKey === column.key);
|
|
9728
|
+
let selectedValues = [];
|
|
9729
|
+
if (existingIndex !== -1) {
|
|
9730
|
+
const existing = filters[existingIndex];
|
|
9731
|
+
selectedValues = Array.isArray(existing.value) ? [...existing.value] : [];
|
|
9732
|
+
}
|
|
9733
|
+
else {
|
|
9734
|
+
// If no filter exists, start with all values
|
|
9735
|
+
selectedValues = this.getUniqueColumnValues(column).map(v => v.value);
|
|
9736
|
+
}
|
|
9737
|
+
if (checked) {
|
|
9738
|
+
// Add value if not present
|
|
9739
|
+
if (!selectedValues.includes(value)) {
|
|
9740
|
+
selectedValues.push(value);
|
|
9741
|
+
}
|
|
9742
|
+
}
|
|
9743
|
+
else {
|
|
9744
|
+
// Remove value
|
|
9745
|
+
selectedValues = selectedValues.filter(v => v !== value);
|
|
9746
|
+
}
|
|
9747
|
+
// Apply filter
|
|
9748
|
+
if (selectedValues.length === 0 || selectedValues.length === this.getUniqueColumnValues(column).length) {
|
|
9749
|
+
// Remove filter if all or none selected
|
|
9750
|
+
if (existingIndex !== -1) {
|
|
9751
|
+
filters.splice(existingIndex, 1);
|
|
9752
|
+
}
|
|
9753
|
+
}
|
|
9754
|
+
else {
|
|
9755
|
+
const filter = {
|
|
9756
|
+
columnKey: column.key,
|
|
9757
|
+
value: selectedValues,
|
|
9758
|
+
operator: 'in'
|
|
9759
|
+
};
|
|
9760
|
+
if (existingIndex !== -1) {
|
|
9761
|
+
filters[existingIndex] = filter;
|
|
9762
|
+
}
|
|
9763
|
+
else {
|
|
9764
|
+
filters.push(filter);
|
|
9765
|
+
}
|
|
9766
|
+
}
|
|
9767
|
+
this.columnFilters.set([...filters]);
|
|
9768
|
+
this.emitEvent('columnFilter', filters, column);
|
|
9769
|
+
this.applyFiltersToData();
|
|
9770
|
+
}
|
|
9771
|
+
selectAllFilterValues(column) {
|
|
9772
|
+
const filters = this.columnFilters();
|
|
9773
|
+
const existingIndex = filters.findIndex(f => f.columnKey === column.key);
|
|
9774
|
+
if (existingIndex !== -1) {
|
|
9775
|
+
filters.splice(existingIndex, 1);
|
|
9776
|
+
this.columnFilters.set([...filters]);
|
|
9777
|
+
this.emitEvent('columnFilter', filters, column);
|
|
9778
|
+
this.applyFiltersToData();
|
|
9779
|
+
}
|
|
9780
|
+
}
|
|
9781
|
+
deselectAllFilterValues(column) {
|
|
9782
|
+
const filters = this.columnFilters();
|
|
9783
|
+
const existingIndex = filters.findIndex(f => f.columnKey === column.key);
|
|
9784
|
+
const filter = {
|
|
9785
|
+
columnKey: column.key,
|
|
9786
|
+
value: [],
|
|
9787
|
+
operator: 'in'
|
|
9788
|
+
};
|
|
9789
|
+
if (existingIndex !== -1) {
|
|
9790
|
+
filters[existingIndex] = filter;
|
|
9791
|
+
}
|
|
9792
|
+
else {
|
|
9793
|
+
filters.push(filter);
|
|
9794
|
+
}
|
|
9795
|
+
this.columnFilters.set([...filters]);
|
|
9796
|
+
this.emitEvent('columnFilter', filters, column);
|
|
9797
|
+
this.applyFiltersToData();
|
|
9798
|
+
}
|
|
9799
|
+
applyFiltersToData() {
|
|
9800
|
+
let filtered = [...this.internalData()];
|
|
9801
|
+
const filters = this.columnFilters();
|
|
9802
|
+
filters.forEach(filter => {
|
|
9803
|
+
const column = this.mergedConfig().columns.find(c => c.key === filter.columnKey);
|
|
9804
|
+
if (!column)
|
|
9805
|
+
return;
|
|
9806
|
+
filtered = filtered.filter(row => {
|
|
9807
|
+
const value = column.valueGetter
|
|
9808
|
+
? this.getNestedValue(row, column.valueGetter)
|
|
9809
|
+
: row[column.key];
|
|
9810
|
+
if (filter.operator === 'in' && Array.isArray(filter.value)) {
|
|
9811
|
+
return filter.value.includes(value);
|
|
9812
|
+
}
|
|
9813
|
+
return true;
|
|
9814
|
+
});
|
|
9815
|
+
});
|
|
9816
|
+
this.filteredData.set(filtered);
|
|
9817
|
+
this.totalItems.set(filtered.length);
|
|
9818
|
+
this.totalPages.set(Math.ceil(filtered.length / this.pageSize()));
|
|
9819
|
+
}
|
|
9052
9820
|
// ===== Utility Methods =====
|
|
9053
9821
|
emitEvent(type, data, column, action) {
|
|
9054
|
-
this.gridEvent.emit({
|
|
9822
|
+
this.gridEvent.emit({
|
|
9823
|
+
type,
|
|
9824
|
+
data,
|
|
9825
|
+
column,
|
|
9826
|
+
action,
|
|
9827
|
+
filters: this.columnFilters(),
|
|
9828
|
+
sorts: this.columnSorts()
|
|
9829
|
+
});
|
|
9055
9830
|
}
|
|
9056
9831
|
/**
|
|
9057
9832
|
* Get nested value from an object
|
|
@@ -9284,7 +10059,7 @@ class CideEleDataGridComponent {
|
|
|
9284
10059
|
return this.mergedConfig().scroll;
|
|
9285
10060
|
}
|
|
9286
10061
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDataGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9287
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleDataGridComponent, isStandalone: true, selector: "cide-ele-data-grid", inputs: { config: "config", templateRenderers: "templateRenderers", customFormatters: "customFormatters", actionHandlers: "actionHandlers", serverSidePagination: "serverSidePagination", totalServerItems: "totalServerItems", currentServerPage: "currentServerPage", currentServerPageSize: "currentServerPageSize", dragDropEnabled: "dragDropEnabled" }, outputs: { gridEvent: "gridEvent" }, usesOnChanges: true, ngImport: i0, template: " <!-- Data Grid Component -->\n <div class=\"data-grid-container tw-bg-white tw-shadow tw-rounded-lg tw-overflow-visible tw-flex tw-flex-col\" \n [ngClass]=\"[\n mergedConfig().tableClass || '',\n mergedConfig().fullHeight ? 'tw-h-full' : '',\n isDragDropEnabled() ? 'drag-drop-enabled' : '',\n isTreeEnabled() ? 'tree-enabled' : ''\n ]\">\n \n <!-- Header Section -->\n @if (mergedConfig().title || mergedConfig().subtitle) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n @if (mergedConfig().title) {\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-900\">\n {{ mergedConfig().title }}\n </h3>\n }\n @if (mergedConfig().subtitle) {\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-0.5\">\n {{ mergedConfig().subtitle }}\n </p>\n }\n </div>\n }\n\n <!-- Search Section -->\n @if (searchConfig.enabled) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <!-- Search Input -->\n <div class=\"tw-max-w-md\">\n <cide-ele-input [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\" id=\"search-input\" type=\"text\"\n [ngModel]=\"searchQuery()\"\n (ngModelChange)=\"updateSearchQuery($event)\"\n [placeholder]=\"searchConfig.placeholder\"\n [disabled]=\"loading() || isRefreshing()\"\n leadingIcon=\"search\"\n fill=\"outline\">\n </cide-ele-input>\n </div>\n \n <!-- Drag Order Actions -->\n @if (isDragDropEnabled() && (isDragging() || hasOrderChanged())) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <button cideEleButton \n variant=\"outline\" \n size=\"sm\" \n type=\"button\"\n (click)=\"onActionClick(null, { key: 'reset-order', label: 'Reset Order', icon: 'undo', variant: 'outline', onClick: 'resetOrder' })\"\n class=\"tw-text-blue-700 tw-border-blue-300 hover:tw-bg-blue-100\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">undo</cide-ele-icon>\n Reset Order\n </button>\n <button cideEleButton \n variant=\"primary\" \n size=\"sm\" \n type=\"button\"\n (click)=\"onActionClick(null, { key: 'save-order', label: 'Save Order', icon: 'save', variant: 'primary', onClick: 'saveOrder' })\"\n class=\"tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">save</cide-ele-icon>\n Save Order\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Table Section -->\n <div class=\"tw-overflow-x-auto tw-relative\"\n [ngClass]=\"{\n 'tw-flex-1 tw-min-h-0': mergedConfig().fullHeight,\n 'tw-overflow-y-auto': scrollConfig?.enabled,\n 'tw-max-h-full': scrollConfig?.enabled\n }\"\n [style.maxHeight]=\"scrollConfig?.enabled ? scrollConfig?.maxHeight : null\"\n [style.minHeight]=\"scrollConfig?.enabled ? scrollConfig?.minHeight : null\">\n <table class=\"tw-min-w-full tw-divide-y tw-divide-gray-200 tw-h-full tw-table-fixed\"\n [class.empty-table]=\"displayedData.length === 0\"\n [ngClass]=\"{\n 'tw-table-striped': mergedConfig().striped,\n 'tw-border': mergedConfig().bordered,\n 'tw-table-sm': mergedConfig().compact\n }\"\n style=\"table-layout: fixed;\">\n \n <!-- Table Header -->\n <thead class=\"tw-bg-gray-50\" \n [ngClass]=\"[\n mergedConfig().headerClass || '',\n scrollConfig?.enabled && scrollConfig?.stickyHeader ? 'tw-sticky tw-top-0 tw-z-10' : ''\n ]\">\n <tr>\n @for (column of columns; track column.key) {\n <th\n class=\"tw-px-3 tw-py-2 tw-text-left tw-text-xs tw-font-medium tw-text-gray-500 tw-uppercase tw-tracking-wider tw-truncate\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : ''\n ]\"\n [title]=\"column.header\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table Body -->\n <tbody class=\"tw-bg-white tw-divide-y tw-divide-gray-200\">\n @if (loading() || isRefreshing() || pageChangeLoading()) {\n <!-- Skeleton Loading Rows -->\n @for (skeletonItem of getSkeletonArray(); track $index) {\n <tr class=\"tw-animate-pulse tw-border-b tw-border-gray-200\">\n @for (column of columns; track column.key) {\n <td class=\"tw-px-3 tw-py-2 tw-whitespace-nowrap\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width)\n ]\">\n <div class=\"tw-h-2 tw-bg-gray-200 tw-rounded tw-w-3/4\"></div>\n </td>\n }\n </tr>\n }\n } @else {\n @for (item of displayedData; track trackByFn($index, item)) {\n <tr class=\"tw-group hover:tw-bg-gray-50 tw-border-b-2 tw-border-gray-200\"\n [ngClass]=\"[\n mergedConfig().rowClass || '',\n isRefreshing() ? 'tw-opacity-60 tw-pointer-events-none' : '',\n isDragDropEnabled() ? 'tw-cursor-move tw-border-2 tw-border-transparent' : '',\n !isDragDropEnabled() ? 'tw-transition-colors tw-duration-150' : '',\n isTreeEnabled() ? getTreeLevelClass(item) : ''\n ]\"\n [style.border-color]=\"isDragOverRow === $index ? '#3b82f6' : 'transparent'\"\n [style.background-color]=\"isDragOverRow === $index ? '#eff6ff' : ''\"\n (click)=\"onRowClick(item)\"\n (keydown.enter)=\"onRowClick(item)\"\n (keydown.space)=\"onRowClick(item)\"\n [class.tw-cursor-pointer]=\"mergedConfig().onRowClick && !isDragDropEnabled()\"\n [tabindex]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 0 : -1\"\n [attr.role]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'button' : null\"\n [attr.aria-label]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'Select row' : null\"\n [draggable]=\"isDragDropEnabled()\"\n (dragstart)=\"onDragStart($event, item, $index)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event, item, $index)\"\n (dragend)=\"onDragEnd($event)\">\n \n @for (column of columns; track column.key) {\n <td class=\"tw-pr-3 tw-py-1 tw-relative\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n mergedConfig().cellClass || '',\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : '',\n column.truncate !== false ? 'tw-whitespace-nowrap' : 'tw-whitespace-normal'\n ]\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : 'none'\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : '100px'\">\n <!-- Tree Expand/Collapse Button (only for first column when tree is enabled) -->\n @if (isTreeEnabled() && $index === 0) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <!-- Tree Indentation -->\n <div class=\"tw-flex tw-items-center\">\n @if (hasChildren(item)) {\n <button \n variant=\"outline\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, { key: 'toggle-expand', label: 'Toggle', icon: '', variant: 'ghost', onClick: 'toggle-expand' }); $event.stopPropagation()\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-text-gray-500 hover:tw-text-gray-700 tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [title]=\"isItemExpanded(item) ? 'Collapse' : 'Expand'\">\n <cide-ele-icon \n class=\"tw-w-3 tw-h-3\"\n [class.tw-transition-transform]=\"!isDragDropEnabled()\"\n [class.tw-rotate-90]=\"isItemExpanded(item)\"\n size=\"xs\">\n chevron_right\n </cide-ele-icon>\n </button>\n } @else {\n <div class=\"tw-w-8 tw-h-5 tw-flex tw-items-center tw-justify-center\">\n <!-- <div class=\"tw-w-1 tw-h-1 tw-bg-gray-300 tw-rounded-full\"></div> -->\n </div>\n }\n </div>\n \n <!-- Cell Content -->\n <div class=\"tw-flex-1 tw-w-full\">\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- Default rendering -->\n @else {\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\n }\n }\n </div>\n </div>\n } @else {\n <!-- Regular cell content (non-tree or non-first column) -->\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- Default rendering -->\n @else {\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\n }\n }\n }\n </td>\n }\n </tr>\n }\n \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"columns.length\" class=\"tw-px-6 tw-py-22 tw-text-center tw-h-full tw-align-middle\">\n <div class=\"tw-text-gray-500 tw-flex tw-flex-col tw-items-center tw-justify-center tw-min-h-[300px]\">\n <svg class=\"tw-mx-auto tw-h-12 tw-w-12 tw-text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"></path>\n </svg>\n <h3 class=\"tw-mt-2 tw-text-sm tw-font-medium tw-text-gray-900\">No data found</h3>\n <p class=\"tw-mt-1 tw-text-sm tw-text-gray-500\">\n @if (searchQuery()) {\n No results match your search criteria.\n } @else {\n There are no items to display.\n }\n </p>\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination Section -->\n @if (paginationConfig.enabled && totalItems() > 0) {\n <div class=\"tw-px-3 tw-py-2 tw-border-t tw-border-gray-200 tw-bg-white tw-relative tw-z-20\"\n [class.tw-opacity-60]=\"isRefreshing()\">\n \n <!-- Results Info and Page Size -->\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-2 sm:tw-space-y-0\">\n \n <!-- Results Info -->\n @if (paginationConfig.showPageInfo) {\n <div class=\"tw-flex tw-items-center tw-space-x-4\">\n <p class=\"tw-text-sm tw-text-gray-700\">\n Showing {{ getItemRangeText() }} results\n </p>\n \n <!-- Page Size Selector -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\n <div class=\"tw-w-20 tw-relative\">\n <cide-ele-select\n [labelHide]=\"true\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"updatePageSize($event)\"\n [options]=\"getPageSizeOptions()\"\n [disabled]=\"isRefreshing()\"\n fill=\"outline\"\n size=\"sm\"\n class=\"tw-z-30\">\n </cide-ele-select>\n </div>\n </div>\n </div>\n }\n\n <!-- Pagination Controls -->\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\n \n <!-- Previous/Next and Page Numbers -->\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n \n <!-- First Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(1)\"\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00AB\u00AB\n </button>\n \n <!-- Previous Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"previousPage()\"\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u2039\n </button>\n \n <!-- Page Numbers -->\n @for (page of getEnhancedPageNumbers(); track page) {\n @if (page === '...') {\n <span class=\"tw-px-2 tw-py-2 tw-text-sm tw-text-gray-500\">...</span>\n } @else {\n <button\n cideEleButton\n [variant]=\"currentPage() === page ? 'primary' : 'outline'\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(page)\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-border tw-rounded-md tw-transition-colors\"\n [ngClass]=\"{\n 'tw-bg-blue-600 tw-text-white tw-border-blue-600': currentPage() === page,\n 'tw-bg-white tw-text-gray-700 tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\n }\">\n {{ page }}\n </button>\n }\n }\n \n <!-- Next Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"nextPage()\"\n [disabled]=\"!hasNextPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u203A\n </button>\n \n <!-- Last Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(totalPages())\"\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00BB\u00BB\n </button>\n \n </div>\n\n <!-- Quick Jump and Refresh -->\n @if (paginationConfig.showQuickJump || paginationConfig.showRefresh) {\n <div class=\"tw-flex tw-items-center tw-space-x-2 tw-border-l tw-border-gray-200 tw-pl-3\">\n \n <!-- Quick Jump -->\n @if (paginationConfig.showQuickJump) {\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n <span class=\"tw-text-sm tw-text-gray-700\">Go to:</span>\n <div class=\"tw-w-20\">\n <cide-ele-input id=\"jump-to-page-input\" type=\"number\" [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\"\n [(ngModel)]=\"jumpToPage\" [min]=\"1\" [max]=\"totalPages()\"\n [disabled]=\"isRefreshing()\"\n (keydown.enter)=\"onJumpToPage()\">\n </cide-ele-input>\n </div>\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onJumpToPage()\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-bg-gray-100 tw-text-gray-700 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n Go\n </button>\n </div>\n }\n \n <!-- Refresh Button -->\n @if (paginationConfig.showRefresh) {\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onRefresh()\"\n [disabled]=\"isRefreshing()\"\n class=\"!tw-w-10 !tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-600 tw-bg-gray-100 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n @if (isRefreshing()) {\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n } @else {\n <svg class=\"tw-h-4 tw-w-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"></path>\n </svg>\n }\n </button>\n }\n </div>\n }\n \n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".data-grid-container{width:100%;display:flex;flex-direction:column;min-height:400px}.data-grid-container.tw-h-full{height:100%;min-height:100%}.data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container .tw-overflow-x-auto.tw-flex-1{flex:1;min-height:0}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar{height:6px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af}.data-grid-container table{min-height:300px}.data-grid-container.tw-h-full table,.data-grid-container.tw-h-full tbody{height:100%}.data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#f9fafb}.data-grid-container.tw-table-sm th,.data-grid-container.tw-table-sm td{padding:.5rem 1rem}.data-grid-container tbody tr:hover{background-color:#f3f4f6}.data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:#e5f3ff}.data-grid-container.loading-overlay{position:relative}.data-grid-container.loading-overlay:after{content:\"\";position:absolute;inset:0;background:#fffc;z-index:10}.data-grid-container .tw-animate-pulse div{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.data-grid-container .action-buttons{display:flex;gap:.25rem}.data-grid-container .action-buttons button{transition:all .15s ease-in-out}.data-grid-container .action-buttons button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.data-grid-container .action-buttons button:disabled{cursor:not-allowed;opacity:.5}.data-grid-container .pagination-controls button{transition:all .15s ease-in-out}.data-grid-container .pagination-controls button:hover:not(:disabled){transform:translateY(-1px)}.data-grid-container .pagination-controls button:disabled{cursor:not-allowed}.data-grid-container .pagination-controls button.active{box-shadow:0 2px 4px #3b82f64d}.data-grid-container .pagination-controls input[type=number]{transition:border-color .15s ease-in-out}.data-grid-container .pagination-controls input[type=number]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .status-badge{font-weight:500;letter-spacing:.025em}.data-grid-container .status-badge.active{background-color:#d1fae5;color:#065f46}.data-grid-container .status-badge.inactive{background-color:#fee2e2;color:#991b1b}.data-grid-container .search-input{position:relative}.data-grid-container .search-input input{transition:all .15s ease-in-out}.data-grid-container .search-input input:focus{box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .search-input .search-icon{pointer-events:none}.data-grid-container .empty-state{padding:3rem 1.5rem;text-align:center}.data-grid-container .empty-state svg{margin:0 auto 1rem;opacity:.4}.data-grid-container .empty-state h3{margin-bottom:.5rem}.data-grid-container.tw-h-full tbody tr:only-child td{height:100%;vertical-align:middle}.data-grid-container.tw-h-full table.empty-table{height:100%}.data-grid-container.tw-h-full tbody tr:only-child{height:100%}@media (max-width: 640px){.data-grid-container .tw-px-6{padding-left:1rem;padding-right:1rem}.data-grid-container .pagination-controls{flex-direction:column;gap:1rem}.data-grid-container .pagination-controls .flex{justify-content:center}.data-grid-container .pagination-info{display:none}.data-grid-container .search-actions{flex-direction:column;gap:1rem}}@media (prefers-color-scheme: dark){.data-grid-container.dark-mode{background-color:#1f2937;color:#f9fafb}.data-grid-container.dark-mode table{background-color:#1f2937}.data-grid-container.dark-mode thead{background-color:#374151}.data-grid-container.dark-mode tbody tr{border-color:#374151}.data-grid-container.dark-mode tbody tr:hover{background-color:#374151}.data-grid-container.dark-mode .tw-text-gray-900{color:#f9fafb!important}.data-grid-container.dark-mode .tw-text-gray-600{color:#d1d5db!important}.data-grid-container.dark-mode .tw-text-gray-500{color:#9ca3af!important}}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:#f3f4f6}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed #3b82f6;border-radius:4px}.data-grid-container.drag-drop-enabled tbody tr.tw-bg-blue-50{background-color:#eff6ffe6}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]{cursor:move;position:relative}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover{background-color:#f8fafc;box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:active{cursor:grabbing}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]{position:relative}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#60a5fa);z-index:10}.data-grid-container.tree-enabled tbody tr td:first-child{position:relative}.data-grid-container.tree-enabled tbody tr td:first-child button{transition:all .15s ease}.data-grid-container.tree-enabled tbody tr td:first-child button:hover{background-color:#f3f4f6;border-radius:2px}.data-grid-container.tree-enabled tbody tr td:first-child button svg{transition:transform .2s ease}.data-grid-container.tree-enabled tbody tr[style*=padding-left]{border-left:2px solid transparent}.data-grid-container.tree-enabled tbody tr[style*=padding-left]:hover{border-left-color:#e5e7eb}.tree-level-0{border-bottom:2px solid #e5e7eb!important;background-color:#fff}.tree-level-0:hover{background-color:#f5f5f5!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid #d1d5db!important;background-color:#fafafa}.tree-level-1:hover{background-color:#f1f5f9!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid #d1d5db!important;background-color:#f9fafb}.tree-level-2:hover{background-color:#f3f4f6!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid #e5e7eb!important;background-color:#fefefe}.tree-level-deep:hover{background-color:#f9fafb!important}.tree-level-deep td:first-child{font-weight:300}table td{box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}table td.tw-overflow-hidden{overflow:hidden;text-overflow:ellipsis}table td:first-child{min-width:150px}table td:first-child>div{display:flex;align-items:center;min-width:0}table td:first-child>div .tw-flex{min-width:0}table td .tw-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CideInputComponent, selector: "cide-ele-input", inputs: ["fill", "label", "labelHide", "disabled", "clearInput", "labelPlacement", "labelDir", "placeholder", "leadingIcon", "trailingIcon", "helperText", "helperTextCollapse", "hideHelperAndErrorText", "errorText", "maxlength", "minlength", "required", "autocapitalize", "autocomplete", "type", "width", "id", "ngModel", "option", "min", "max", "size"], outputs: ["ngModelChange"] }, { kind: "component", type: CideSelectComponent, selector: "cide-ele-select", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey"], outputs: ["ngModelChange", "change", "searchChange"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }] });
|
|
10062
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleDataGridComponent, isStandalone: true, selector: "cide-ele-data-grid", inputs: { config: "config", templateRenderers: "templateRenderers", customFormatters: "customFormatters", actionHandlers: "actionHandlers", serverSidePagination: "serverSidePagination", totalServerItems: "totalServerItems", currentServerPage: "currentServerPage", currentServerPageSize: "currentServerPageSize", dragDropEnabled: "dragDropEnabled" }, outputs: { gridEvent: "gridEvent" }, usesOnChanges: true, ngImport: i0, template: " <!-- Data Grid Component -->\r\n <div class=\"data-grid-container tw-bg-white tw-shadow tw-rounded-lg tw-overflow-visible tw-flex tw-flex-col\" \r\n [ngClass]=\"[\r\n mergedConfig().tableClass || '',\r\n mergedConfig().fullHeight ? 'tw-h-full' : '',\r\n isDragDropEnabled() ? 'drag-drop-enabled' : '',\r\n isTreeEnabled() ? 'tree-enabled' : ''\r\n ]\">\r\n \r\n <!-- Header Section -->\r\n @if (mergedConfig().title || mergedConfig().subtitle) {\r\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\r\n @if (mergedConfig().title) {\r\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-900\">\r\n {{ mergedConfig().title }}\r\n </h3>\r\n }\r\n @if (mergedConfig().subtitle) {\r\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-0.5\">\r\n {{ mergedConfig().subtitle }}\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Search Section -->\r\n @if (searchConfig.enabled) {\r\n <div class=\"tw-px-3 tw-py-1.5 tw-border-b tw-border-gray-200\">\r\n <div class=\"tw-flex tw-items-center tw-justify-between\">\r\n <!-- Left Side: Search Input and Action Icons -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-1.5\">\r\n <!-- Search Input - Apple Style -->\r\n <div class=\"tw-max-w-md data-grid-search-input\">\r\n <cide-ele-input [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\" id=\"search-input\" type=\"text\"\r\n [ngModel]=\"searchQuery()\"\r\n (ngModelChange)=\"updateSearchQuery($event)\"\r\n [placeholder]=\"searchConfig.placeholder\"\r\n [disabled]=\"loading() || isRefreshing()\"\r\n leadingIcon=\"search\"\r\n fill=\"outline\"\r\n size=\"sm\">\r\n </cide-ele-input>\r\n </div>\r\n \r\n <!-- Action Icons (Filter, Sort, Download) - Apple Style Compact -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-1 data-grid-action-buttons\">\r\n <!-- Filter Button -->\r\n <button type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200\"\r\n title=\"Filter\"\r\n (click)=\"onActionClick(null, { key: 'filter', label: 'Filter', icon: 'filter_list', variant: 'ghost', onClick: 'filter' })\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Sort Button -->\r\n <button type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200\"\r\n title=\"Sort\"\r\n (click)=\"onActionClick(null, { key: 'sort', label: 'Sort', icon: 'swap_vert', variant: 'ghost', onClick: 'sort' })\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">swap_vert</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Download/Export Button -->\r\n <button type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200\"\r\n title=\"Download\"\r\n (click)=\"onActionClick(null, { key: 'download', label: 'Download', icon: 'file_download', variant: 'ghost', onClick: 'download' })\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <!-- Right Side: Drag Order Actions -->\r\n @if (isDragDropEnabled() && (isDragging() || hasOrderChanged())) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <button cideEleButton \r\n variant=\"outline\" \r\n size=\"sm\" \r\n type=\"button\"\r\n (click)=\"onActionClick(null, { key: 'reset-order', label: 'Reset Order', icon: 'undo', variant: 'outline', onClick: 'resetOrder' })\"\r\n class=\"tw-text-blue-700 tw-border-blue-300 hover:tw-bg-blue-100\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">undo</cide-ele-icon>\r\n Reset Order\r\n </button>\r\n <button cideEleButton \r\n variant=\"primary\" \r\n size=\"sm\" \r\n type=\"button\"\r\n (click)=\"onActionClick(null, { key: 'save-order', label: 'Save Order', icon: 'save', variant: 'primary', onClick: 'saveOrder' })\"\r\n class=\"tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">save</cide-ele-icon>\r\n Save Order\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Table Section -->\r\n <div class=\"tw-overflow-x-auto tw-relative\"\r\n [ngClass]=\"{\r\n 'tw-flex-1 tw-min-h-0': mergedConfig().fullHeight,\r\n 'tw-overflow-y-auto': scrollConfig?.enabled,\r\n 'tw-max-h-full': scrollConfig?.enabled\r\n }\"\r\n [style.maxHeight]=\"scrollConfig?.enabled ? scrollConfig?.maxHeight : null\"\r\n [style.minHeight]=\"scrollConfig?.enabled ? scrollConfig?.minHeight : null\">\r\n <table class=\"tw-min-w-full tw-divide-y tw-divide-gray-200 tw-h-full tw-table-fixed\"\r\n [class.empty-table]=\"displayedData.length === 0\"\r\n [ngClass]=\"{\r\n 'tw-table-striped': mergedConfig().striped,\r\n 'tw-border': mergedConfig().bordered,\r\n 'tw-table-sm': mergedConfig().compact\r\n }\"\r\n style=\"table-layout: fixed;\">\r\n \r\n <!-- Table Header -->\r\n <thead class=\"tw-bg-gray-50\" \r\n [ngClass]=\"[\r\n mergedConfig().headerClass || '',\r\n scrollConfig?.enabled && scrollConfig?.stickyHeader ? 'tw-sticky tw-top-0 tw-z-10' : ''\r\n ]\">\r\n <tr>\r\n @for (column of visibleColumns(); track column.key) {\r\n <th\r\n class=\"tw-px-3 tw-py-2 tw-text-left tw-text-xs tw-font-medium tw-text-gray-500 tw-uppercase tw-tracking-wider tw-relative\"\r\n [ngClass]=\"[\r\n getColumnWidthClass(column.width),\r\n getColumnMaxWidthClass(column.width),\r\n column.align === 'center' ? 'tw-text-center' : '',\r\n column.align === 'right' ? 'tw-text-right' : ''\r\n ]\"\r\n [title]=\"column.header\">\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\r\n <span class=\"tw-truncate tw-flex-1\">{{ column.header }}</span>\r\n \r\n <!-- Active Filter Indicator -->\r\n @if (isColumnFiltered(column.key)) {\r\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded tw-bg-blue-100 tw-text-blue-700 tw-text-xs tw-font-medium tw-mr-1\">\r\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-mr-0.5\">filter_alt</cide-ele-icon>\r\n {{ getActiveFilterCount(column.key) }}\r\n </div>\r\n }\r\n \r\n <!-- Column Menu Trigger (Three Dots Icon) -->\r\n @if (mergedConfig().columnMenu?.enabled) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-rounded tw-text-gray-600 hover:tw-text-gray-800 hover:tw-bg-gray-100 tw-transition-all column-menu-trigger\"\r\n [class.tw-text-blue-600]=\"isColumnMenuOpen(column.key) || isColumnFiltered(column.key)\"\r\n [class.tw-bg-blue-50]=\"isColumnFiltered(column.key)\"\r\n (click)=\"toggleColumnMenu(column.key, $event)\"\r\n title=\"Column options\">\r\n <cide-ele-icon class=\"tw-w-5 tw-h-4\">more_vert</cide-ele-icon>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Column Menu Dropdown -->\r\n @if (isColumnMenuOpen(column.key)) {\r\n <div class=\"column-menu-dropdown tw-absolute tw-z-50 tw-mt-2 tw-w-56 tw-rounded-lg tw-shadow-lg tw-bg-white tw-ring-1 tw-ring-black tw-ring-opacity-5\">\r\n <div class=\"tw-py-1\">\r\n <!-- Sort Options -->\r\n @if (mergedConfig().columnMenu?.showSort && column.sortable !== false) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnSort(column, 'asc')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\r\n Ascending\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnSort(column, 'desc')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\r\n Descending\r\n </button>\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n }\r\n\r\n <!-- Filter Option -->\r\n @if (mergedConfig().columnMenu?.showFilter && column.filterable !== false) {\r\n <!-- Check if there's a custom filter renderer template -->\r\n @if (column.filterRenderer && templateRenderers[column.filterRenderer]) {\r\n <div class=\"tw-px-4 tw-py-2\">\r\n <ng-container [ngTemplateOutlet]=\"$any(templateRenderers[column.filterRenderer])\"\r\n [ngTemplateOutletContext]=\"{ $implicit: column, column: column, onFilter: onColumnFilter.bind(this) }\">\r\n </ng-container>\r\n </div>\r\n } @else {\r\n <!-- Excel-style Filter Button -->\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-justify-between tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n [class.tw-bg-blue-50]=\"isFilterPanelOpen(column.key)\"\r\n (click)=\"toggleFilterPanel(column.key, $event)\">\r\n <div class=\"tw-flex tw-items-center\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">filter_list</cide-ele-icon>\r\n Filter\r\n </div>\r\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-gray-400\">\r\n {{ isFilterPanelOpen(column.key) ? 'expand_less' : 'expand_more' }}\r\n </cide-ele-icon>\r\n </button>\r\n \r\n <!-- Excel-style Filter Panel -->\r\n @if (isFilterPanelOpen(column.key)) {\r\n <div class=\"tw-px-2 tw-py-2 tw-bg-gray-50\" (click)=\"$event.stopPropagation()\">\r\n <!-- Search box -->\r\n <div class=\"tw-px-2 tw-mb-2\">\r\n <input\r\n type=\"text\"\r\n class=\"tw-w-full tw-px-2 tw-py-1 tw-text-xs tw-border tw-border-gray-300 tw-rounded focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500\"\r\n placeholder=\"Search...\"\r\n [(ngModel)]=\"filterSearchTerm\"\r\n (click)=\"$event.stopPropagation()\">\r\n </div>\r\n \r\n <!-- Select All / Deselect All -->\r\n <div class=\"tw-px-2 tw-mb-1 tw-flex tw-gap-2\">\r\n <button\r\n type=\"button\"\r\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\r\n (click)=\"selectAllFilterValues(column); $event.stopPropagation()\">\r\n Select All\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-400\">|</span>\r\n <button\r\n type=\"button\"\r\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\r\n (click)=\"deselectAllFilterValues(column); $event.stopPropagation()\">\r\n Clear\r\n </button>\r\n </div>\r\n \r\n <!-- Filter values list -->\r\n <div class=\"tw-max-h-48 tw-overflow-y-auto\">\r\n @for (item of getUniqueColumnValues(column); track item.value) {\r\n <label class=\"tw-flex tw-items-center tw-px-2 tw-py-1 tw-text-xs hover:tw-bg-white tw-cursor-pointer tw-rounded\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"tw-mr-2 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\r\n [checked]=\"item.checked\"\r\n (change)=\"toggleFilterValue(column, item.value, $any($event.target).checked)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <span class=\"tw-flex-1 tw-truncate\">{{ item.label }}</span>\r\n <span class=\"tw-text-gray-400 tw-ml-1\">({{ item.count }})</span>\r\n </label>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n }\r\n\r\n <!-- Autosize Option -->\r\n @if (mergedConfig().columnMenu?.showAutosize) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAutosize(column)\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">fit_screen</cide-ele-icon>\r\n Autosize\r\n </button>\r\n }\r\n\r\n <!-- Group By Column Option -->\r\n @if (mergedConfig().columnMenu?.showGroupBy && column.groupable !== false) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnGroupBy(column)\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">group_work</cide-ele-icon>\r\n Group By Column\r\n </button>\r\n }\r\n\r\n <!-- Manage Columns Option -->\r\n @if (mergedConfig().columnMenu?.showManageColumns) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onManageColumns()\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">view_column</cide-ele-icon>\r\n Manage Columns\r\n </button>\r\n }\r\n\r\n <!-- Reset Columns Option -->\r\n @if (mergedConfig().columnMenu?.showResetColumns) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnReset()\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">refresh</cide-ele-icon>\r\n Reset Columns\r\n </button>\r\n }\r\n\r\n <!-- Hide Column Option -->\r\n @if (mergedConfig().columnMenu?.showHideColumn && column.hideable !== false) {\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnHide(column)\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">visibility_off</cide-ele-icon>\r\n Hide Column\r\n </button>\r\n }\r\n\r\n <!-- Aggregation Select Option -->\r\n @if (mergedConfig().columnMenu?.showAggregation && column.aggregatable !== false) {\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n <div class=\"tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase\">Aggregation</div>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'sum')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">functions</cide-ele-icon>\r\n Sum\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'avg')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">analytics</cide-ele-icon>\r\n Average\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'count')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">tag</cide-ele-icon>\r\n Count\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'min')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\r\n Min\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'max')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\r\n Max\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <!-- Table Body -->\r\n <tbody class=\"tw-bg-white tw-divide-y tw-divide-gray-200\">\r\n @if (loading() || isRefreshing() || pageChangeLoading()) {\r\n <!-- Skeleton Loading Rows -->\r\n @for (skeletonItem of getSkeletonArray(); track $index) {\r\n <tr class=\"tw-animate-pulse tw-border-b tw-border-gray-200\">\r\n @for (column of columns; track column.key) {\r\n <td class=\"tw-px-3 tw-py-2 tw-whitespace-nowrap\"\r\n [ngClass]=\"[\r\n getColumnWidthClass(column.width),\r\n getColumnMaxWidthClass(column.width)\r\n ]\">\r\n <div class=\"tw-h-2 tw-bg-gray-200 tw-rounded tw-w-3/4\"></div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n } @else {\r\n @for (item of displayedData; track trackByFn($index, item)) {\r\n <tr class=\"tw-group hover:tw-bg-gray-50 tw-border-b-2 tw-border-gray-200\"\r\n [ngClass]=\"[\r\n mergedConfig().rowClass || '',\r\n isRefreshing() ? 'tw-opacity-60 tw-pointer-events-none' : '',\r\n isDragDropEnabled() ? 'tw-cursor-move tw-border-2 tw-border-transparent' : '',\r\n !isDragDropEnabled() ? 'tw-transition-colors tw-duration-150' : '',\r\n isTreeEnabled() ? getTreeLevelClass(item) : ''\r\n ]\"\r\n [style.border-color]=\"isDragOverRow === $index ? '#3b82f6' : 'transparent'\"\r\n [style.background-color]=\"isDragOverRow === $index ? '#eff6ff' : ''\"\r\n (click)=\"onRowClick(item)\"\r\n (keydown.enter)=\"onRowClick(item)\"\r\n (keydown.space)=\"onRowClick(item)\"\r\n [class.tw-cursor-pointer]=\"mergedConfig().onRowClick && !isDragDropEnabled()\"\r\n [tabindex]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 0 : -1\"\r\n [attr.role]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'button' : null\"\r\n [attr.aria-label]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'Select row' : null\"\r\n [draggable]=\"isDragDropEnabled()\"\r\n (dragstart)=\"onDragStart($event, item, $index)\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event, item, $index)\"\r\n (dragend)=\"onDragEnd($event)\">\r\n \r\n @for (column of columns; track column.key) {\r\n <td class=\"tw-pr-3 tw-py-1 tw-relative\"\r\n [ngClass]=\"[\r\n getColumnWidthClass(column.width),\r\n getColumnMaxWidthClass(column.width),\r\n mergedConfig().cellClass || '',\r\n column.align === 'center' ? 'tw-text-center' : '',\r\n column.align === 'right' ? 'tw-text-right' : '',\r\n column.truncate !== false ? 'tw-whitespace-nowrap' : 'tw-whitespace-normal'\r\n ]\"\r\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\r\n [style.maxWidth]=\"getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : 'none'\"\r\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : '100px'\">\r\n <!-- Tree Expand/Collapse Button (only for first column when tree is enabled) -->\r\n @if (isTreeEnabled() && $index === 0) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <!-- Tree Indentation -->\r\n <div class=\"tw-flex tw-items-center\">\r\n @if (hasChildren(item)) {\r\n <button \r\n variant=\"outline\"\r\n size=\"xs\"\r\n type=\"button\"\r\n (click)=\"onActionClick(item, { key: 'toggle-expand', label: 'Toggle', icon: '', variant: 'ghost', onClick: 'toggle-expand' }); $event.stopPropagation()\"\r\n class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-text-gray-500 hover:tw-text-gray-700 tw-rounded\"\r\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\r\n [title]=\"isItemExpanded(item) ? 'Collapse' : 'Expand'\">\r\n <cide-ele-icon \r\n class=\"tw-w-3 tw-h-3\"\r\n [class.tw-transition-transform]=\"!isDragDropEnabled()\"\r\n [class.tw-rotate-90]=\"isItemExpanded(item)\"\r\n size=\"xs\">\r\n chevron_right\r\n </cide-ele-icon>\r\n </button>\r\n } @else {\r\n <div class=\"tw-w-8 tw-h-5 tw-flex tw-items-center tw-justify-center\">\r\n <!-- <div class=\"tw-w-1 tw-h-1 tw-bg-gray-300 tw-rounded-full\"></div> -->\r\n </div>\r\n }\r\n </div>\r\n \r\n <!-- Cell Content -->\r\n <div class=\"tw-flex-1 tw-w-full\">\r\n @if (column.type === 'text') {\r\n <p class=\"tw-text-sm tw-text-gray-900\"\r\n [class.tw-truncate]=\"column.truncate\"\r\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </p>\r\n } @else if (column.type === 'number') {\r\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'date') {\r\n <span class=\"tw-text-sm tw-text-gray-600\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'boolean') {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\r\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\r\n </span>\r\n } @else if (column.type === 'status' && column.statusConfig) {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\r\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\r\n </span>\r\n } @else if (column.type === 'actions' && column.actions) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n @for (action of column.actions; track action.key) {\r\n <button\r\n cideEleButton\r\n [variant]=\"action.variant || 'ghost'\"\r\n size=\"xs\"\r\n type=\"button\"\r\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\r\n [title]=\"action.tooltip || action.label\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\r\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\r\n [ngClass]=\"{\r\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\r\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\r\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\r\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\r\n }\">\r\n @if (action.icon) {\r\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\r\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\r\n </svg>\r\n }\r\n {{ action.label }}\r\n </button>\r\n }\r\n </div>\r\n } @else if (column.type === 'custom') {\r\n <!-- Template Renderer -->\r\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\r\n <ng-container \r\n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\r\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\r\n </ng-container>\r\n }\r\n <!-- Default rendering -->\r\n @else {\r\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\r\n }\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Regular cell content (non-tree or non-first column) -->\r\n @if (column.type === 'text') {\r\n <p class=\"tw-text-sm tw-text-gray-900\"\r\n [class.tw-truncate]=\"column.truncate\"\r\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </p>\r\n } @else if (column.type === 'number') {\r\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'date') {\r\n <span class=\"tw-text-sm tw-text-gray-600\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'boolean') {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\r\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\r\n </span>\r\n } @else if (column.type === 'status' && column.statusConfig) {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\r\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\r\n </span>\r\n } @else if (column.type === 'actions' && column.actions) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n @for (action of column.actions; track action.key) {\r\n <button\r\n cideEleButton\r\n [variant]=\"action.variant || 'ghost'\"\r\n size=\"xs\"\r\n type=\"button\"\r\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\r\n [title]=\"action.tooltip || action.label\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\r\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\r\n [ngClass]=\"{\r\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\r\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\r\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\r\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\r\n }\">\r\n @if (action.icon) {\r\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\r\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\r\n </svg>\r\n }\r\n {{ action.label }}\r\n </button>\r\n }\r\n </div>\r\n } @else if (column.type === 'custom') {\r\n <!-- Template Renderer -->\r\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\r\n <ng-container \r\n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\r\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\r\n </ng-container>\r\n }\r\n <!-- Default rendering -->\r\n @else {\r\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\r\n }\r\n }\r\n }\r\n </td>\r\n }\r\n </tr>\r\n }\r\n \r\n <!-- Empty State -->\r\n @if (displayedData.length === 0) {\r\n <tr class=\"tw-h-full\">\r\n <td [attr.colspan]=\"columns.length\" class=\"tw-px-6 tw-py-22 tw-text-center tw-h-full tw-align-middle\">\r\n <div class=\"tw-text-gray-500 tw-flex tw-flex-col tw-items-center tw-justify-center tw-min-h-[300px]\">\r\n <svg class=\"tw-mx-auto tw-h-12 tw-w-12 tw-text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"></path>\r\n </svg>\r\n <h3 class=\"tw-mt-2 tw-text-sm tw-font-medium tw-text-gray-900\">No data found</h3>\r\n <p class=\"tw-mt-1 tw-text-sm tw-text-gray-500\">\r\n @if (searchQuery()) {\r\n No results match your search criteria.\r\n } @else {\r\n There are no items to display.\r\n }\r\n </p>\r\n </div>\r\n </td>\r\n </tr>\r\n }\r\n }\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <!-- Pagination Section -->\r\n @if (paginationConfig.enabled && totalItems() > 0) {\r\n <div class=\"tw-px-3 tw-py-0 tw-border-t tw-border-gray-200 tw-bg-white tw-relative tw-z-20\"\r\n [class.tw-opacity-60]=\"isRefreshing()\">\r\n \r\n <!-- Results Info and Page Size -->\r\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-1 sm:tw-space-y-0\">\r\n \r\n <!-- Results Info -->\r\n @if (paginationConfig.showPageInfo) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\r\n <p class=\"tw-text-sm tw-text-gray-700\">\r\n Showing {{ getItemRangeText() }} results\r\n </p>\r\n \r\n <!-- Page Size Selector -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-500\">view_list</cide-ele-icon>\r\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\r\n <div class=\"tw-w-16 tw-relative\">\r\n <cide-ele-select\r\n [labelHide]=\"true\"\r\n [ngModel]=\"pageSize()\"\r\n (ngModelChange)=\"updatePageSize($event)\"\r\n [options]=\"getPageSizeOptions()\"\r\n [disabled]=\"isRefreshing()\"\r\n fill=\"outline\"\r\n size=\"xs\"\r\n class=\"tw-z-30\">\r\n </cide-ele-select>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Pagination Controls -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n \r\n <!-- Previous/Next and Page Numbers -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\r\n \r\n <!-- First Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"onPageChange(1)\"\r\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"First page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">first_page</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Previous Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"previousPage()\"\r\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Previous page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_left</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Page Numbers -->\r\n @for (page of getEnhancedPageNumbers(); track page) {\r\n @if (page === '...') {\r\n <span class=\"tw-px-2 tw-py-2 tw-text-sm tw-text-gray-500\">...</span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n (click)=\"onPageChange(page)\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-p-0 tw-rounded tw-text-xs tw-font-medium tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n [ngClass]=\"{\r\n 'tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700': currentPage() === page,\r\n 'tw-bg-white tw-text-gray-700 tw-border tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\r\n }\"\r\n [title]=\"'Page ' + page\">\r\n {{ page }}\r\n </button>\r\n }\r\n }\r\n \r\n <!-- Next Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"nextPage()\"\r\n [disabled]=\"!hasNextPage() || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Next page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_right</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Last Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"onPageChange(totalPages())\"\r\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Last page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">last_page</cide-ele-icon>\r\n </button>\r\n \r\n </div>\r\n\r\n <!-- Quick Jump and Refresh -->\r\n @if (paginationConfig.showQuickJump || paginationConfig.showRefresh) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-1 tw-border-l tw-border-gray-200 tw-pl-2\">\r\n \r\n <!-- Quick Jump -->\r\n @if (paginationConfig.showQuickJump) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\r\n <span class=\"tw-text-sm tw-text-gray-700\">Go to:</span>\r\n <div class=\"tw-w-16\">\r\n <cide-ele-input id=\"jump-to-page-input\" type=\"number\" [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\"\r\n [(ngModel)]=\"jumpToPage\" [min]=\"1\" [max]=\"totalPages()\"\r\n [disabled]=\"isRefreshing()\"\r\n size=\"xs\"\r\n (keydown.enter)=\"onJumpToPage()\">\r\n </cide-ele-input>\r\n </div>\r\n <button\r\n type=\"button\"\r\n (click)=\"onJumpToPage()\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-p-0 tw-rounded tw-text-xs tw-font-medium tw-bg-gray-100 tw-text-gray-700 tw-border tw-border-gray-300 hover:tw-bg-gray-200 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Go to page\">\r\n Go\r\n </button>\r\n </div>\r\n }\r\n \r\n <!-- Refresh Button -->\r\n @if (paginationConfig.showRefresh) {\r\n <button\r\n type=\"button\"\r\n (click)=\"onRefresh()\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Refresh\">\r\n @if (isRefreshing()) {\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4 tw-animate-spin\">refresh</cide-ele-icon>\r\n } @else {\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">refresh</cide-ele-icon>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n \r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".data-grid-container{width:100%;display:flex;flex-direction:column;min-height:400px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:14px;color:#1f2937;background-color:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px #0000000a,0 1px 2px #00000005;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.data-grid-container.tw-h-full{height:100%;min-height:100%}.data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container .tw-overflow-x-auto.tw-flex-1{flex:1;min-height:0}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar{height:6px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af}.data-grid-container table{min-height:300px;border-collapse:separate;border-spacing:0;width:100%;background-color:#fff}.data-grid-container thead{background:linear-gradient(180deg,#fafafa,#f7f7f7);border-bottom:1px solid rgba(0,0,0,.06);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.data-grid-container thead th{background:transparent;color:#6b7280;font-weight:500;font-size:12px;text-transform:none;letter-spacing:-.01em;padding:4px 10px;border-bottom:1px solid rgba(0,0,0,.06);text-align:left;white-space:nowrap;position:relative;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container thead th:first-child{padding-left:12px}.data-grid-container thead th:last-child{padding-right:12px}.data-grid-container thead th:hover{background-color:#00000005}.data-grid-container thead th .column-menu-trigger{opacity:1;transition:all .2s cubic-bezier(.4,0,.2,1);margin-left:4px;cursor:pointer;padding:2px;border-radius:4px}.data-grid-container thead th .column-menu-trigger:hover{background-color:#0000000f}.data-grid-container tbody{background-color:#fff}.data-grid-container tbody td{padding:6px 10px;border-bottom:1px solid rgba(0,0,0,.03);color:#1f2937;font-size:13px;vertical-align:middle;line-height:1.5;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody td:first-child{padding-left:12px}.data-grid-container tbody td:last-child{padding-right:12px}.data-grid-container tbody tr{background-color:#fff;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody tr:hover{background-color:#00000005;transform:scale(1.001)}.data-grid-container tbody tr:hover td{border-bottom-color:#0000000a}.data-grid-container tbody tr:active{background-color:#00000008;transform:scale(.999)}.data-grid-container.tw-h-full table,.data-grid-container.tw-h-full tbody{height:100%}.data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#00000004}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover{background-color:#00000008;transform:scale(1.001)}.data-grid-container.tw-table-sm thead th{padding:8px 10px;font-size:11px}.data-grid-container.tw-table-sm thead th:first-child{padding-left:16px}.data-grid-container.tw-table-sm thead th:last-child{padding-right:16px}.data-grid-container.tw-table-sm tbody td{padding:8px 10px;font-size:12px;line-height:1.4}.data-grid-container.tw-table-sm tbody td:first-child{padding-left:16px}.data-grid-container.tw-table-sm tbody td:last-child{padding-right:16px}.data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:#e5f3ff}.data-grid-container.loading-overlay{position:relative}.data-grid-container.loading-overlay:after{content:\"\";position:absolute;inset:0;background:#ffffffd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:10;animation:fadeIn .2s cubic-bezier(.4,0,.2,1)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.data-grid-container .tw-animate-pulse div{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.data-grid-container .data-grid-action-buttons button{position:relative;border:1px solid rgba(0,0,0,.08);background:transparent;outline:none;cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:6px;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-action-buttons button cide-ele-icon{color:#6b7280;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-action-buttons button:hover:not(:disabled){background-color:#0000000a;border-color:#0000001f}.data-grid-container .data-grid-action-buttons button:hover:not(:disabled) cide-ele-icon{color:#374151;transform:scale(1.05)}.data-grid-container .data-grid-action-buttons button:active:not(:disabled){background-color:#00000014;border-color:#00000026;transform:scale(.95)}.data-grid-container .data-grid-action-buttons button:active:not(:disabled) cide-ele-icon{transform:scale(.98)}.data-grid-container .data-grid-action-buttons button:focus-visible{outline:none;border-color:#3b82f64d;box-shadow:0 0 0 3px #3b82f61f}.data-grid-container .data-grid-action-buttons button:disabled{cursor:not-allowed;opacity:.3;background:transparent!important;border-color:#0000000d}.data-grid-container .action-buttons{display:flex;gap:.25rem}.data-grid-container .action-buttons button{transition:all .15s ease-in-out}.data-grid-container .action-buttons button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.data-grid-container .action-buttons button:disabled{cursor:not-allowed;opacity:.5}.data-grid-container .pagination-controls{padding:12px 20px;border-top:1px solid rgba(0,0,0,.06);background:linear-gradient(180deg,#fafafa,#f7f7f7)}.data-grid-container .pagination-controls button{transition:all .2s cubic-bezier(.4,0,.2,1);border-radius:6px}.data-grid-container .pagination-controls button:hover:not(:disabled){background-color:#0000000a;transform:scale(1.02)}.data-grid-container .pagination-controls button:active:not(:disabled){transform:scale(.98);background-color:#00000014}.data-grid-container .pagination-controls button:disabled{cursor:not-allowed;opacity:.3}.data-grid-container .pagination-controls button.active{background-color:#3b82f61a;color:#3b82f6;font-weight:500;box-shadow:0 0 0 1px #3b82f633}.data-grid-container .pagination-controls input[type=number]{transition:all .2s cubic-bezier(.4,0,.2,1);border:1px solid rgba(0,0,0,.06);border-radius:6px}.data-grid-container .pagination-controls input[type=number]:hover{border-color:#0000001a}.data-grid-container .pagination-controls input[type=number]:focus{outline:none;border-color:#3b82f64d;box-shadow:0 0 0 3px #3b82f614;background-color:#fff}.data-grid-container .status-badge{font-weight:500;letter-spacing:-.01em;padding:4px 12px;border-radius:12px;font-size:12px;display:inline-flex;align-items:center;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .status-badge.active{background-color:#34d39926;color:#059669;border:1px solid rgba(52,211,153,.3)}.data-grid-container .status-badge.active:hover{background-color:#34d39933}.data-grid-container .status-badge.inactive{background-color:#f8717126;color:#dc2626;border:1px solid rgba(248,113,113,.3)}.data-grid-container .status-badge.inactive:hover{background-color:#f8717133}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper{transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper:hover:not(:has(input:disabled)){background-color:#00000005}.data-grid-container .data-grid-search-input ::ng-deep input{font-size:13px;font-weight:400;color:#1f2937;transition:all .2s cubic-bezier(.4,0,.2,1);border-radius:8px}.data-grid-container .data-grid-search-input ::ng-deep input::placeholder{color:#9ca3af;font-weight:400}.data-grid-container .data-grid-search-input ::ng-deep input:focus{outline:none;border-color:#3b82f64d;box-shadow:0 0 0 3px #3b82f614;background-color:#fff}.data-grid-container .data-grid-search-input ::ng-deep input:disabled{background-color:#00000005;color:#9ca3af;cursor:not-allowed}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-leading-icon cide-ele-icon{color:#9ca3af;transition:color .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-search-input ::ng-deep input:focus~.cide-input-leading-icon cide-ele-icon,.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper:has(input:focus) .cide-input-leading-icon cide-ele-icon{color:#6b7280}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper[data-fill=outline]{border:1px solid #e5e7eb;background-color:#fafafa;border-radius:8px}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper[data-fill=outline]:hover:not(:has(input:disabled)){border-color:#d1d5db;background-color:#fff}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper[data-fill=outline]:has(input:focus){border-color:#3b82f64d;background-color:#fff}.data-grid-container .search-input{position:relative}.data-grid-container .search-input input{transition:all .15s ease-in-out}.data-grid-container .search-input input:focus{box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .search-input .search-icon{pointer-events:none}.data-grid-container .column-menu-dropdown{animation:dropdownFadeIn .15s cubic-bezier(.4,0,.2,1);box-shadow:0 10px 25px #0000001a,0 4px 10px #0000000d;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(0,0,0,.05)}.data-grid-container .column-menu-dropdown button{font-size:13px;font-weight:400;letter-spacing:-.01em;text-align:left;transition:all .15s cubic-bezier(.4,0,.2,1)}.data-grid-container .column-menu-dropdown button:hover{background-color:#3b82f60d;color:#1f2937}.data-grid-container .column-menu-dropdown button:hover cide-ele-icon{color:#3b82f6}.data-grid-container .column-menu-dropdown button:active{background-color:#3b82f61a;transform:scale(.98)}.data-grid-container .column-menu-dropdown .tw-uppercase{letter-spacing:.05em}@keyframes dropdownFadeIn{0%{opacity:0;transform:translateY(-8px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.data-grid-container .empty-state{padding:4rem 2rem;text-align:center}.data-grid-container .empty-state svg{margin:0 auto 1.5rem;opacity:.3;transition:all .3s cubic-bezier(.4,0,.2,1)}.data-grid-container .empty-state svg:hover{opacity:.5;transform:scale(1.05)}.data-grid-container .empty-state h3{margin-bottom:.75rem;font-weight:600;color:#374151;font-size:16px;letter-spacing:-.01em}.data-grid-container .empty-state p{color:#6b7280;font-size:14px;line-height:1.5}.data-grid-container.tw-h-full tbody tr:only-child td{height:100%;vertical-align:middle}.data-grid-container.tw-h-full table.empty-table{height:100%}.data-grid-container.tw-h-full tbody tr:only-child{height:100%}@media (max-width: 640px){.data-grid-container .tw-px-6{padding-left:1rem;padding-right:1rem}.data-grid-container .pagination-controls{flex-direction:column;gap:1rem}.data-grid-container .pagination-controls .flex{justify-content:center}.data-grid-container .pagination-info{display:none}.data-grid-container .search-actions{flex-direction:column;gap:1rem}}@media (prefers-color-scheme: dark){.data-grid-container.dark-mode{background-color:#1f2937;color:#f9fafb}.data-grid-container.dark-mode table{background-color:#1f2937}.data-grid-container.dark-mode thead{background-color:#374151}.data-grid-container.dark-mode tbody tr{border-color:#374151}.data-grid-container.dark-mode tbody tr:hover{background-color:#374151}.data-grid-container.dark-mode .tw-text-gray-900{color:#f9fafb!important}.data-grid-container.dark-mode .tw-text-gray-600{color:#d1d5db!important}.data-grid-container.dark-mode .tw-text-gray-500{color:#9ca3af!important}}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:#f3f4f6}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed #3b82f6;border-radius:4px}.data-grid-container.drag-drop-enabled tbody tr.tw-bg-blue-50{background-color:#eff6ffe6}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]{cursor:move;position:relative}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover{background-color:#f8fafc;box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:active{cursor:grabbing}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]{position:relative}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#60a5fa);z-index:10}.data-grid-container.tree-enabled tbody tr td:first-child{position:relative}.data-grid-container.tree-enabled tbody tr td:first-child button{transition:all .15s ease}.data-grid-container.tree-enabled tbody tr td:first-child button:hover{background-color:#f3f4f6;border-radius:2px}.data-grid-container.tree-enabled tbody tr td:first-child button svg{transition:transform .2s ease}.data-grid-container.tree-enabled tbody tr[style*=padding-left]{border-left:2px solid transparent}.data-grid-container.tree-enabled tbody tr[style*=padding-left]:hover{border-left-color:#e5e7eb}.tree-level-0{border-bottom:2px solid #e5e7eb!important;background-color:#fff}.tree-level-0:hover{background-color:#f5f5f5!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid #d1d5db!important;background-color:#fafafa}.tree-level-1:hover{background-color:#f1f5f9!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid #d1d5db!important;background-color:#f9fafb}.tree-level-2:hover{background-color:#f3f4f6!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid #e5e7eb!important;background-color:#fefefe}.tree-level-deep:hover{background-color:#f9fafb!important}.tree-level-deep td:first-child{font-weight:300}table td{box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}table td.tw-overflow-hidden{overflow:hidden;text-overflow:ellipsis}table td:first-child{min-width:150px}table td:first-child>div{display:flex;align-items:center;min-width:0}table td:first-child>div .tw-flex{min-width:0}table td .tw-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CideInputComponent, selector: "cide-ele-input", inputs: ["fill", "label", "labelHide", "disabled", "clearInput", "labelPlacement", "labelDir", "placeholder", "leadingIcon", "trailingIcon", "helperText", "helperTextCollapse", "hideHelperAndErrorText", "errorText", "maxlength", "minlength", "required", "autocapitalize", "autocomplete", "type", "width", "id", "ngModel", "option", "min", "max", "size"], outputs: ["ngModelChange"] }, { kind: "component", type: CideSelectComponent, selector: "cide-ele-select", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey"], outputs: ["ngModelChange", "change", "searchChange"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }] });
|
|
9288
10063
|
}
|
|
9289
10064
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDataGridComponent, decorators: [{
|
|
9290
10065
|
type: Component,
|
|
@@ -9296,7 +10071,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
9296
10071
|
CideSelectComponent,
|
|
9297
10072
|
CideIconComponent,
|
|
9298
10073
|
CideEleButtonComponent
|
|
9299
|
-
], template: " <!-- Data Grid Component -->\n <div class=\"data-grid-container tw-bg-white tw-shadow tw-rounded-lg tw-overflow-visible tw-flex tw-flex-col\" \n [ngClass]=\"[\n mergedConfig().tableClass || '',\n mergedConfig().fullHeight ? 'tw-h-full' : '',\n isDragDropEnabled() ? 'drag-drop-enabled' : '',\n isTreeEnabled() ? 'tree-enabled' : ''\n ]\">\n \n <!-- Header Section -->\n @if (mergedConfig().title || mergedConfig().subtitle) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n @if (mergedConfig().title) {\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-900\">\n {{ mergedConfig().title }}\n </h3>\n }\n @if (mergedConfig().subtitle) {\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-0.5\">\n {{ mergedConfig().subtitle }}\n </p>\n }\n </div>\n }\n\n <!-- Search Section -->\n @if (searchConfig.enabled) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <!-- Search Input -->\n <div class=\"tw-max-w-md\">\n <cide-ele-input [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\" id=\"search-input\" type=\"text\"\n [ngModel]=\"searchQuery()\"\n (ngModelChange)=\"updateSearchQuery($event)\"\n [placeholder]=\"searchConfig.placeholder\"\n [disabled]=\"loading() || isRefreshing()\"\n leadingIcon=\"search\"\n fill=\"outline\">\n </cide-ele-input>\n </div>\n \n <!-- Drag Order Actions -->\n @if (isDragDropEnabled() && (isDragging() || hasOrderChanged())) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <button cideEleButton \n variant=\"outline\" \n size=\"sm\" \n type=\"button\"\n (click)=\"onActionClick(null, { key: 'reset-order', label: 'Reset Order', icon: 'undo', variant: 'outline', onClick: 'resetOrder' })\"\n class=\"tw-text-blue-700 tw-border-blue-300 hover:tw-bg-blue-100\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">undo</cide-ele-icon>\n Reset Order\n </button>\n <button cideEleButton \n variant=\"primary\" \n size=\"sm\" \n type=\"button\"\n (click)=\"onActionClick(null, { key: 'save-order', label: 'Save Order', icon: 'save', variant: 'primary', onClick: 'saveOrder' })\"\n class=\"tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">save</cide-ele-icon>\n Save Order\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Table Section -->\n <div class=\"tw-overflow-x-auto tw-relative\"\n [ngClass]=\"{\n 'tw-flex-1 tw-min-h-0': mergedConfig().fullHeight,\n 'tw-overflow-y-auto': scrollConfig?.enabled,\n 'tw-max-h-full': scrollConfig?.enabled\n }\"\n [style.maxHeight]=\"scrollConfig?.enabled ? scrollConfig?.maxHeight : null\"\n [style.minHeight]=\"scrollConfig?.enabled ? scrollConfig?.minHeight : null\">\n <table class=\"tw-min-w-full tw-divide-y tw-divide-gray-200 tw-h-full tw-table-fixed\"\n [class.empty-table]=\"displayedData.length === 0\"\n [ngClass]=\"{\n 'tw-table-striped': mergedConfig().striped,\n 'tw-border': mergedConfig().bordered,\n 'tw-table-sm': mergedConfig().compact\n }\"\n style=\"table-layout: fixed;\">\n \n <!-- Table Header -->\n <thead class=\"tw-bg-gray-50\" \n [ngClass]=\"[\n mergedConfig().headerClass || '',\n scrollConfig?.enabled && scrollConfig?.stickyHeader ? 'tw-sticky tw-top-0 tw-z-10' : ''\n ]\">\n <tr>\n @for (column of columns; track column.key) {\n <th\n class=\"tw-px-3 tw-py-2 tw-text-left tw-text-xs tw-font-medium tw-text-gray-500 tw-uppercase tw-tracking-wider tw-truncate\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : ''\n ]\"\n [title]=\"column.header\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table Body -->\n <tbody class=\"tw-bg-white tw-divide-y tw-divide-gray-200\">\n @if (loading() || isRefreshing() || pageChangeLoading()) {\n <!-- Skeleton Loading Rows -->\n @for (skeletonItem of getSkeletonArray(); track $index) {\n <tr class=\"tw-animate-pulse tw-border-b tw-border-gray-200\">\n @for (column of columns; track column.key) {\n <td class=\"tw-px-3 tw-py-2 tw-whitespace-nowrap\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width)\n ]\">\n <div class=\"tw-h-2 tw-bg-gray-200 tw-rounded tw-w-3/4\"></div>\n </td>\n }\n </tr>\n }\n } @else {\n @for (item of displayedData; track trackByFn($index, item)) {\n <tr class=\"tw-group hover:tw-bg-gray-50 tw-border-b-2 tw-border-gray-200\"\n [ngClass]=\"[\n mergedConfig().rowClass || '',\n isRefreshing() ? 'tw-opacity-60 tw-pointer-events-none' : '',\n isDragDropEnabled() ? 'tw-cursor-move tw-border-2 tw-border-transparent' : '',\n !isDragDropEnabled() ? 'tw-transition-colors tw-duration-150' : '',\n isTreeEnabled() ? getTreeLevelClass(item) : ''\n ]\"\n [style.border-color]=\"isDragOverRow === $index ? '#3b82f6' : 'transparent'\"\n [style.background-color]=\"isDragOverRow === $index ? '#eff6ff' : ''\"\n (click)=\"onRowClick(item)\"\n (keydown.enter)=\"onRowClick(item)\"\n (keydown.space)=\"onRowClick(item)\"\n [class.tw-cursor-pointer]=\"mergedConfig().onRowClick && !isDragDropEnabled()\"\n [tabindex]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 0 : -1\"\n [attr.role]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'button' : null\"\n [attr.aria-label]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'Select row' : null\"\n [draggable]=\"isDragDropEnabled()\"\n (dragstart)=\"onDragStart($event, item, $index)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event, item, $index)\"\n (dragend)=\"onDragEnd($event)\">\n \n @for (column of columns; track column.key) {\n <td class=\"tw-pr-3 tw-py-1 tw-relative\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n mergedConfig().cellClass || '',\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : '',\n column.truncate !== false ? 'tw-whitespace-nowrap' : 'tw-whitespace-normal'\n ]\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : 'none'\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : '100px'\">\n <!-- Tree Expand/Collapse Button (only for first column when tree is enabled) -->\n @if (isTreeEnabled() && $index === 0) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <!-- Tree Indentation -->\n <div class=\"tw-flex tw-items-center\">\n @if (hasChildren(item)) {\n <button \n variant=\"outline\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, { key: 'toggle-expand', label: 'Toggle', icon: '', variant: 'ghost', onClick: 'toggle-expand' }); $event.stopPropagation()\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-text-gray-500 hover:tw-text-gray-700 tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [title]=\"isItemExpanded(item) ? 'Collapse' : 'Expand'\">\n <cide-ele-icon \n class=\"tw-w-3 tw-h-3\"\n [class.tw-transition-transform]=\"!isDragDropEnabled()\"\n [class.tw-rotate-90]=\"isItemExpanded(item)\"\n size=\"xs\">\n chevron_right\n </cide-ele-icon>\n </button>\n } @else {\n <div class=\"tw-w-8 tw-h-5 tw-flex tw-items-center tw-justify-center\">\n <!-- <div class=\"tw-w-1 tw-h-1 tw-bg-gray-300 tw-rounded-full\"></div> -->\n </div>\n }\n </div>\n \n <!-- Cell Content -->\n <div class=\"tw-flex-1 tw-w-full\">\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- Default rendering -->\n @else {\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\n }\n }\n </div>\n </div>\n } @else {\n <!-- Regular cell content (non-tree or non-first column) -->\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- Default rendering -->\n @else {\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\n }\n }\n }\n </td>\n }\n </tr>\n }\n \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"columns.length\" class=\"tw-px-6 tw-py-22 tw-text-center tw-h-full tw-align-middle\">\n <div class=\"tw-text-gray-500 tw-flex tw-flex-col tw-items-center tw-justify-center tw-min-h-[300px]\">\n <svg class=\"tw-mx-auto tw-h-12 tw-w-12 tw-text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"></path>\n </svg>\n <h3 class=\"tw-mt-2 tw-text-sm tw-font-medium tw-text-gray-900\">No data found</h3>\n <p class=\"tw-mt-1 tw-text-sm tw-text-gray-500\">\n @if (searchQuery()) {\n No results match your search criteria.\n } @else {\n There are no items to display.\n }\n </p>\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination Section -->\n @if (paginationConfig.enabled && totalItems() > 0) {\n <div class=\"tw-px-3 tw-py-2 tw-border-t tw-border-gray-200 tw-bg-white tw-relative tw-z-20\"\n [class.tw-opacity-60]=\"isRefreshing()\">\n \n <!-- Results Info and Page Size -->\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-2 sm:tw-space-y-0\">\n \n <!-- Results Info -->\n @if (paginationConfig.showPageInfo) {\n <div class=\"tw-flex tw-items-center tw-space-x-4\">\n <p class=\"tw-text-sm tw-text-gray-700\">\n Showing {{ getItemRangeText() }} results\n </p>\n \n <!-- Page Size Selector -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\n <div class=\"tw-w-20 tw-relative\">\n <cide-ele-select\n [labelHide]=\"true\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"updatePageSize($event)\"\n [options]=\"getPageSizeOptions()\"\n [disabled]=\"isRefreshing()\"\n fill=\"outline\"\n size=\"sm\"\n class=\"tw-z-30\">\n </cide-ele-select>\n </div>\n </div>\n </div>\n }\n\n <!-- Pagination Controls -->\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\n \n <!-- Previous/Next and Page Numbers -->\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n \n <!-- First Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(1)\"\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00AB\u00AB\n </button>\n \n <!-- Previous Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"previousPage()\"\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u2039\n </button>\n \n <!-- Page Numbers -->\n @for (page of getEnhancedPageNumbers(); track page) {\n @if (page === '...') {\n <span class=\"tw-px-2 tw-py-2 tw-text-sm tw-text-gray-500\">...</span>\n } @else {\n <button\n cideEleButton\n [variant]=\"currentPage() === page ? 'primary' : 'outline'\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(page)\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-border tw-rounded-md tw-transition-colors\"\n [ngClass]=\"{\n 'tw-bg-blue-600 tw-text-white tw-border-blue-600': currentPage() === page,\n 'tw-bg-white tw-text-gray-700 tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\n }\">\n {{ page }}\n </button>\n }\n }\n \n <!-- Next Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"nextPage()\"\n [disabled]=\"!hasNextPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u203A\n </button>\n \n <!-- Last Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(totalPages())\"\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00BB\u00BB\n </button>\n \n </div>\n\n <!-- Quick Jump and Refresh -->\n @if (paginationConfig.showQuickJump || paginationConfig.showRefresh) {\n <div class=\"tw-flex tw-items-center tw-space-x-2 tw-border-l tw-border-gray-200 tw-pl-3\">\n \n <!-- Quick Jump -->\n @if (paginationConfig.showQuickJump) {\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n <span class=\"tw-text-sm tw-text-gray-700\">Go to:</span>\n <div class=\"tw-w-20\">\n <cide-ele-input id=\"jump-to-page-input\" type=\"number\" [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\"\n [(ngModel)]=\"jumpToPage\" [min]=\"1\" [max]=\"totalPages()\"\n [disabled]=\"isRefreshing()\"\n (keydown.enter)=\"onJumpToPage()\">\n </cide-ele-input>\n </div>\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onJumpToPage()\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-bg-gray-100 tw-text-gray-700 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n Go\n </button>\n </div>\n }\n \n <!-- Refresh Button -->\n @if (paginationConfig.showRefresh) {\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onRefresh()\"\n [disabled]=\"isRefreshing()\"\n class=\"!tw-w-10 !tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-600 tw-bg-gray-100 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n @if (isRefreshing()) {\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n } @else {\n <svg class=\"tw-h-4 tw-w-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"></path>\n </svg>\n }\n </button>\n }\n </div>\n }\n \n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".data-grid-container{width:100%;display:flex;flex-direction:column;min-height:400px}.data-grid-container.tw-h-full{height:100%;min-height:100%}.data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container .tw-overflow-x-auto.tw-flex-1{flex:1;min-height:0}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar{height:6px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af}.data-grid-container table{min-height:300px}.data-grid-container.tw-h-full table,.data-grid-container.tw-h-full tbody{height:100%}.data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#f9fafb}.data-grid-container.tw-table-sm th,.data-grid-container.tw-table-sm td{padding:.5rem 1rem}.data-grid-container tbody tr:hover{background-color:#f3f4f6}.data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:#e5f3ff}.data-grid-container.loading-overlay{position:relative}.data-grid-container.loading-overlay:after{content:\"\";position:absolute;inset:0;background:#fffc;z-index:10}.data-grid-container .tw-animate-pulse div{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.data-grid-container .action-buttons{display:flex;gap:.25rem}.data-grid-container .action-buttons button{transition:all .15s ease-in-out}.data-grid-container .action-buttons button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.data-grid-container .action-buttons button:disabled{cursor:not-allowed;opacity:.5}.data-grid-container .pagination-controls button{transition:all .15s ease-in-out}.data-grid-container .pagination-controls button:hover:not(:disabled){transform:translateY(-1px)}.data-grid-container .pagination-controls button:disabled{cursor:not-allowed}.data-grid-container .pagination-controls button.active{box-shadow:0 2px 4px #3b82f64d}.data-grid-container .pagination-controls input[type=number]{transition:border-color .15s ease-in-out}.data-grid-container .pagination-controls input[type=number]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .status-badge{font-weight:500;letter-spacing:.025em}.data-grid-container .status-badge.active{background-color:#d1fae5;color:#065f46}.data-grid-container .status-badge.inactive{background-color:#fee2e2;color:#991b1b}.data-grid-container .search-input{position:relative}.data-grid-container .search-input input{transition:all .15s ease-in-out}.data-grid-container .search-input input:focus{box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .search-input .search-icon{pointer-events:none}.data-grid-container .empty-state{padding:3rem 1.5rem;text-align:center}.data-grid-container .empty-state svg{margin:0 auto 1rem;opacity:.4}.data-grid-container .empty-state h3{margin-bottom:.5rem}.data-grid-container.tw-h-full tbody tr:only-child td{height:100%;vertical-align:middle}.data-grid-container.tw-h-full table.empty-table{height:100%}.data-grid-container.tw-h-full tbody tr:only-child{height:100%}@media (max-width: 640px){.data-grid-container .tw-px-6{padding-left:1rem;padding-right:1rem}.data-grid-container .pagination-controls{flex-direction:column;gap:1rem}.data-grid-container .pagination-controls .flex{justify-content:center}.data-grid-container .pagination-info{display:none}.data-grid-container .search-actions{flex-direction:column;gap:1rem}}@media (prefers-color-scheme: dark){.data-grid-container.dark-mode{background-color:#1f2937;color:#f9fafb}.data-grid-container.dark-mode table{background-color:#1f2937}.data-grid-container.dark-mode thead{background-color:#374151}.data-grid-container.dark-mode tbody tr{border-color:#374151}.data-grid-container.dark-mode tbody tr:hover{background-color:#374151}.data-grid-container.dark-mode .tw-text-gray-900{color:#f9fafb!important}.data-grid-container.dark-mode .tw-text-gray-600{color:#d1d5db!important}.data-grid-container.dark-mode .tw-text-gray-500{color:#9ca3af!important}}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:#f3f4f6}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed #3b82f6;border-radius:4px}.data-grid-container.drag-drop-enabled tbody tr.tw-bg-blue-50{background-color:#eff6ffe6}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]{cursor:move;position:relative}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover{background-color:#f8fafc;box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:active{cursor:grabbing}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]{position:relative}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#60a5fa);z-index:10}.data-grid-container.tree-enabled tbody tr td:first-child{position:relative}.data-grid-container.tree-enabled tbody tr td:first-child button{transition:all .15s ease}.data-grid-container.tree-enabled tbody tr td:first-child button:hover{background-color:#f3f4f6;border-radius:2px}.data-grid-container.tree-enabled tbody tr td:first-child button svg{transition:transform .2s ease}.data-grid-container.tree-enabled tbody tr[style*=padding-left]{border-left:2px solid transparent}.data-grid-container.tree-enabled tbody tr[style*=padding-left]:hover{border-left-color:#e5e7eb}.tree-level-0{border-bottom:2px solid #e5e7eb!important;background-color:#fff}.tree-level-0:hover{background-color:#f5f5f5!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid #d1d5db!important;background-color:#fafafa}.tree-level-1:hover{background-color:#f1f5f9!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid #d1d5db!important;background-color:#f9fafb}.tree-level-2:hover{background-color:#f3f4f6!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid #e5e7eb!important;background-color:#fefefe}.tree-level-deep:hover{background-color:#f9fafb!important}.tree-level-deep td:first-child{font-weight:300}table td{box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}table td.tw-overflow-hidden{overflow:hidden;text-overflow:ellipsis}table td:first-child{min-width:150px}table td:first-child>div{display:flex;align-items:center;min-width:0}table td:first-child>div .tw-flex{min-width:0}table td .tw-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}\n"] }]
|
|
10074
|
+
], template: " <!-- Data Grid Component -->\r\n <div class=\"data-grid-container tw-bg-white tw-shadow tw-rounded-lg tw-overflow-visible tw-flex tw-flex-col\" \r\n [ngClass]=\"[\r\n mergedConfig().tableClass || '',\r\n mergedConfig().fullHeight ? 'tw-h-full' : '',\r\n isDragDropEnabled() ? 'drag-drop-enabled' : '',\r\n isTreeEnabled() ? 'tree-enabled' : ''\r\n ]\">\r\n \r\n <!-- Header Section -->\r\n @if (mergedConfig().title || mergedConfig().subtitle) {\r\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\r\n @if (mergedConfig().title) {\r\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-900\">\r\n {{ mergedConfig().title }}\r\n </h3>\r\n }\r\n @if (mergedConfig().subtitle) {\r\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-0.5\">\r\n {{ mergedConfig().subtitle }}\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Search Section -->\r\n @if (searchConfig.enabled) {\r\n <div class=\"tw-px-3 tw-py-1.5 tw-border-b tw-border-gray-200\">\r\n <div class=\"tw-flex tw-items-center tw-justify-between\">\r\n <!-- Left Side: Search Input and Action Icons -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-1.5\">\r\n <!-- Search Input - Apple Style -->\r\n <div class=\"tw-max-w-md data-grid-search-input\">\r\n <cide-ele-input [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\" id=\"search-input\" type=\"text\"\r\n [ngModel]=\"searchQuery()\"\r\n (ngModelChange)=\"updateSearchQuery($event)\"\r\n [placeholder]=\"searchConfig.placeholder\"\r\n [disabled]=\"loading() || isRefreshing()\"\r\n leadingIcon=\"search\"\r\n fill=\"outline\"\r\n size=\"sm\">\r\n </cide-ele-input>\r\n </div>\r\n \r\n <!-- Action Icons (Filter, Sort, Download) - Apple Style Compact -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-1 data-grid-action-buttons\">\r\n <!-- Filter Button -->\r\n <button type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200\"\r\n title=\"Filter\"\r\n (click)=\"onActionClick(null, { key: 'filter', label: 'Filter', icon: 'filter_list', variant: 'ghost', onClick: 'filter' })\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Sort Button -->\r\n <button type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200\"\r\n title=\"Sort\"\r\n (click)=\"onActionClick(null, { key: 'sort', label: 'Sort', icon: 'swap_vert', variant: 'ghost', onClick: 'sort' })\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">swap_vert</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Download/Export Button -->\r\n <button type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200\"\r\n title=\"Download\"\r\n (click)=\"onActionClick(null, { key: 'download', label: 'Download', icon: 'file_download', variant: 'ghost', onClick: 'download' })\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <!-- Right Side: Drag Order Actions -->\r\n @if (isDragDropEnabled() && (isDragging() || hasOrderChanged())) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <button cideEleButton \r\n variant=\"outline\" \r\n size=\"sm\" \r\n type=\"button\"\r\n (click)=\"onActionClick(null, { key: 'reset-order', label: 'Reset Order', icon: 'undo', variant: 'outline', onClick: 'resetOrder' })\"\r\n class=\"tw-text-blue-700 tw-border-blue-300 hover:tw-bg-blue-100\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">undo</cide-ele-icon>\r\n Reset Order\r\n </button>\r\n <button cideEleButton \r\n variant=\"primary\" \r\n size=\"sm\" \r\n type=\"button\"\r\n (click)=\"onActionClick(null, { key: 'save-order', label: 'Save Order', icon: 'save', variant: 'primary', onClick: 'saveOrder' })\"\r\n class=\"tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">save</cide-ele-icon>\r\n Save Order\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Table Section -->\r\n <div class=\"tw-overflow-x-auto tw-relative\"\r\n [ngClass]=\"{\r\n 'tw-flex-1 tw-min-h-0': mergedConfig().fullHeight,\r\n 'tw-overflow-y-auto': scrollConfig?.enabled,\r\n 'tw-max-h-full': scrollConfig?.enabled\r\n }\"\r\n [style.maxHeight]=\"scrollConfig?.enabled ? scrollConfig?.maxHeight : null\"\r\n [style.minHeight]=\"scrollConfig?.enabled ? scrollConfig?.minHeight : null\">\r\n <table class=\"tw-min-w-full tw-divide-y tw-divide-gray-200 tw-h-full tw-table-fixed\"\r\n [class.empty-table]=\"displayedData.length === 0\"\r\n [ngClass]=\"{\r\n 'tw-table-striped': mergedConfig().striped,\r\n 'tw-border': mergedConfig().bordered,\r\n 'tw-table-sm': mergedConfig().compact\r\n }\"\r\n style=\"table-layout: fixed;\">\r\n \r\n <!-- Table Header -->\r\n <thead class=\"tw-bg-gray-50\" \r\n [ngClass]=\"[\r\n mergedConfig().headerClass || '',\r\n scrollConfig?.enabled && scrollConfig?.stickyHeader ? 'tw-sticky tw-top-0 tw-z-10' : ''\r\n ]\">\r\n <tr>\r\n @for (column of visibleColumns(); track column.key) {\r\n <th\r\n class=\"tw-px-3 tw-py-2 tw-text-left tw-text-xs tw-font-medium tw-text-gray-500 tw-uppercase tw-tracking-wider tw-relative\"\r\n [ngClass]=\"[\r\n getColumnWidthClass(column.width),\r\n getColumnMaxWidthClass(column.width),\r\n column.align === 'center' ? 'tw-text-center' : '',\r\n column.align === 'right' ? 'tw-text-right' : ''\r\n ]\"\r\n [title]=\"column.header\">\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\r\n <span class=\"tw-truncate tw-flex-1\">{{ column.header }}</span>\r\n \r\n <!-- Active Filter Indicator -->\r\n @if (isColumnFiltered(column.key)) {\r\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded tw-bg-blue-100 tw-text-blue-700 tw-text-xs tw-font-medium tw-mr-1\">\r\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-mr-0.5\">filter_alt</cide-ele-icon>\r\n {{ getActiveFilterCount(column.key) }}\r\n </div>\r\n }\r\n \r\n <!-- Column Menu Trigger (Three Dots Icon) -->\r\n @if (mergedConfig().columnMenu?.enabled) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-rounded tw-text-gray-600 hover:tw-text-gray-800 hover:tw-bg-gray-100 tw-transition-all column-menu-trigger\"\r\n [class.tw-text-blue-600]=\"isColumnMenuOpen(column.key) || isColumnFiltered(column.key)\"\r\n [class.tw-bg-blue-50]=\"isColumnFiltered(column.key)\"\r\n (click)=\"toggleColumnMenu(column.key, $event)\"\r\n title=\"Column options\">\r\n <cide-ele-icon class=\"tw-w-5 tw-h-4\">more_vert</cide-ele-icon>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Column Menu Dropdown -->\r\n @if (isColumnMenuOpen(column.key)) {\r\n <div class=\"column-menu-dropdown tw-absolute tw-z-50 tw-mt-2 tw-w-56 tw-rounded-lg tw-shadow-lg tw-bg-white tw-ring-1 tw-ring-black tw-ring-opacity-5\">\r\n <div class=\"tw-py-1\">\r\n <!-- Sort Options -->\r\n @if (mergedConfig().columnMenu?.showSort && column.sortable !== false) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnSort(column, 'asc')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\r\n Ascending\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnSort(column, 'desc')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\r\n Descending\r\n </button>\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n }\r\n\r\n <!-- Filter Option -->\r\n @if (mergedConfig().columnMenu?.showFilter && column.filterable !== false) {\r\n <!-- Check if there's a custom filter renderer template -->\r\n @if (column.filterRenderer && templateRenderers[column.filterRenderer]) {\r\n <div class=\"tw-px-4 tw-py-2\">\r\n <ng-container [ngTemplateOutlet]=\"$any(templateRenderers[column.filterRenderer])\"\r\n [ngTemplateOutletContext]=\"{ $implicit: column, column: column, onFilter: onColumnFilter.bind(this) }\">\r\n </ng-container>\r\n </div>\r\n } @else {\r\n <!-- Excel-style Filter Button -->\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-justify-between tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n [class.tw-bg-blue-50]=\"isFilterPanelOpen(column.key)\"\r\n (click)=\"toggleFilterPanel(column.key, $event)\">\r\n <div class=\"tw-flex tw-items-center\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">filter_list</cide-ele-icon>\r\n Filter\r\n </div>\r\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-gray-400\">\r\n {{ isFilterPanelOpen(column.key) ? 'expand_less' : 'expand_more' }}\r\n </cide-ele-icon>\r\n </button>\r\n \r\n <!-- Excel-style Filter Panel -->\r\n @if (isFilterPanelOpen(column.key)) {\r\n <div class=\"tw-px-2 tw-py-2 tw-bg-gray-50\" (click)=\"$event.stopPropagation()\">\r\n <!-- Search box -->\r\n <div class=\"tw-px-2 tw-mb-2\">\r\n <input\r\n type=\"text\"\r\n class=\"tw-w-full tw-px-2 tw-py-1 tw-text-xs tw-border tw-border-gray-300 tw-rounded focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500\"\r\n placeholder=\"Search...\"\r\n [(ngModel)]=\"filterSearchTerm\"\r\n (click)=\"$event.stopPropagation()\">\r\n </div>\r\n \r\n <!-- Select All / Deselect All -->\r\n <div class=\"tw-px-2 tw-mb-1 tw-flex tw-gap-2\">\r\n <button\r\n type=\"button\"\r\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\r\n (click)=\"selectAllFilterValues(column); $event.stopPropagation()\">\r\n Select All\r\n </button>\r\n <span class=\"tw-text-xs tw-text-gray-400\">|</span>\r\n <button\r\n type=\"button\"\r\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\r\n (click)=\"deselectAllFilterValues(column); $event.stopPropagation()\">\r\n Clear\r\n </button>\r\n </div>\r\n \r\n <!-- Filter values list -->\r\n <div class=\"tw-max-h-48 tw-overflow-y-auto\">\r\n @for (item of getUniqueColumnValues(column); track item.value) {\r\n <label class=\"tw-flex tw-items-center tw-px-2 tw-py-1 tw-text-xs hover:tw-bg-white tw-cursor-pointer tw-rounded\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"tw-mr-2 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\r\n [checked]=\"item.checked\"\r\n (change)=\"toggleFilterValue(column, item.value, $any($event.target).checked)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <span class=\"tw-flex-1 tw-truncate\">{{ item.label }}</span>\r\n <span class=\"tw-text-gray-400 tw-ml-1\">({{ item.count }})</span>\r\n </label>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n }\r\n\r\n <!-- Autosize Option -->\r\n @if (mergedConfig().columnMenu?.showAutosize) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAutosize(column)\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">fit_screen</cide-ele-icon>\r\n Autosize\r\n </button>\r\n }\r\n\r\n <!-- Group By Column Option -->\r\n @if (mergedConfig().columnMenu?.showGroupBy && column.groupable !== false) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnGroupBy(column)\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">group_work</cide-ele-icon>\r\n Group By Column\r\n </button>\r\n }\r\n\r\n <!-- Manage Columns Option -->\r\n @if (mergedConfig().columnMenu?.showManageColumns) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onManageColumns()\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">view_column</cide-ele-icon>\r\n Manage Columns\r\n </button>\r\n }\r\n\r\n <!-- Reset Columns Option -->\r\n @if (mergedConfig().columnMenu?.showResetColumns) {\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnReset()\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">refresh</cide-ele-icon>\r\n Reset Columns\r\n </button>\r\n }\r\n\r\n <!-- Hide Column Option -->\r\n @if (mergedConfig().columnMenu?.showHideColumn && column.hideable !== false) {\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnHide(column)\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">visibility_off</cide-ele-icon>\r\n Hide Column\r\n </button>\r\n }\r\n\r\n <!-- Aggregation Select Option -->\r\n @if (mergedConfig().columnMenu?.showAggregation && column.aggregatable !== false) {\r\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\r\n <div class=\"tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase\">Aggregation</div>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'sum')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">functions</cide-ele-icon>\r\n Sum\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'avg')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">analytics</cide-ele-icon>\r\n Average\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'count')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">tag</cide-ele-icon>\r\n Count\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'min')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\r\n Min\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"tw-w-full tw-flex tw-items-center tw-px-4 tw-py-2 tw-text-sm tw-text-gray-700 hover:tw-bg-gray-50 tw-transition-colors\"\r\n (click)=\"onColumnAggregation(column, 'max')\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\r\n Max\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <!-- Table Body -->\r\n <tbody class=\"tw-bg-white tw-divide-y tw-divide-gray-200\">\r\n @if (loading() || isRefreshing() || pageChangeLoading()) {\r\n <!-- Skeleton Loading Rows -->\r\n @for (skeletonItem of getSkeletonArray(); track $index) {\r\n <tr class=\"tw-animate-pulse tw-border-b tw-border-gray-200\">\r\n @for (column of columns; track column.key) {\r\n <td class=\"tw-px-3 tw-py-2 tw-whitespace-nowrap\"\r\n [ngClass]=\"[\r\n getColumnWidthClass(column.width),\r\n getColumnMaxWidthClass(column.width)\r\n ]\">\r\n <div class=\"tw-h-2 tw-bg-gray-200 tw-rounded tw-w-3/4\"></div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n } @else {\r\n @for (item of displayedData; track trackByFn($index, item)) {\r\n <tr class=\"tw-group hover:tw-bg-gray-50 tw-border-b-2 tw-border-gray-200\"\r\n [ngClass]=\"[\r\n mergedConfig().rowClass || '',\r\n isRefreshing() ? 'tw-opacity-60 tw-pointer-events-none' : '',\r\n isDragDropEnabled() ? 'tw-cursor-move tw-border-2 tw-border-transparent' : '',\r\n !isDragDropEnabled() ? 'tw-transition-colors tw-duration-150' : '',\r\n isTreeEnabled() ? getTreeLevelClass(item) : ''\r\n ]\"\r\n [style.border-color]=\"isDragOverRow === $index ? '#3b82f6' : 'transparent'\"\r\n [style.background-color]=\"isDragOverRow === $index ? '#eff6ff' : ''\"\r\n (click)=\"onRowClick(item)\"\r\n (keydown.enter)=\"onRowClick(item)\"\r\n (keydown.space)=\"onRowClick(item)\"\r\n [class.tw-cursor-pointer]=\"mergedConfig().onRowClick && !isDragDropEnabled()\"\r\n [tabindex]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 0 : -1\"\r\n [attr.role]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'button' : null\"\r\n [attr.aria-label]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'Select row' : null\"\r\n [draggable]=\"isDragDropEnabled()\"\r\n (dragstart)=\"onDragStart($event, item, $index)\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event, item, $index)\"\r\n (dragend)=\"onDragEnd($event)\">\r\n \r\n @for (column of columns; track column.key) {\r\n <td class=\"tw-pr-3 tw-py-1 tw-relative\"\r\n [ngClass]=\"[\r\n getColumnWidthClass(column.width),\r\n getColumnMaxWidthClass(column.width),\r\n mergedConfig().cellClass || '',\r\n column.align === 'center' ? 'tw-text-center' : '',\r\n column.align === 'right' ? 'tw-text-right' : '',\r\n column.truncate !== false ? 'tw-whitespace-nowrap' : 'tw-whitespace-normal'\r\n ]\"\r\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\r\n [style.maxWidth]=\"getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : 'none'\"\r\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : '100px'\">\r\n <!-- Tree Expand/Collapse Button (only for first column when tree is enabled) -->\r\n @if (isTreeEnabled() && $index === 0) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <!-- Tree Indentation -->\r\n <div class=\"tw-flex tw-items-center\">\r\n @if (hasChildren(item)) {\r\n <button \r\n variant=\"outline\"\r\n size=\"xs\"\r\n type=\"button\"\r\n (click)=\"onActionClick(item, { key: 'toggle-expand', label: 'Toggle', icon: '', variant: 'ghost', onClick: 'toggle-expand' }); $event.stopPropagation()\"\r\n class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-text-gray-500 hover:tw-text-gray-700 tw-rounded\"\r\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\r\n [title]=\"isItemExpanded(item) ? 'Collapse' : 'Expand'\">\r\n <cide-ele-icon \r\n class=\"tw-w-3 tw-h-3\"\r\n [class.tw-transition-transform]=\"!isDragDropEnabled()\"\r\n [class.tw-rotate-90]=\"isItemExpanded(item)\"\r\n size=\"xs\">\r\n chevron_right\r\n </cide-ele-icon>\r\n </button>\r\n } @else {\r\n <div class=\"tw-w-8 tw-h-5 tw-flex tw-items-center tw-justify-center\">\r\n <!-- <div class=\"tw-w-1 tw-h-1 tw-bg-gray-300 tw-rounded-full\"></div> -->\r\n </div>\r\n }\r\n </div>\r\n \r\n <!-- Cell Content -->\r\n <div class=\"tw-flex-1 tw-w-full\">\r\n @if (column.type === 'text') {\r\n <p class=\"tw-text-sm tw-text-gray-900\"\r\n [class.tw-truncate]=\"column.truncate\"\r\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </p>\r\n } @else if (column.type === 'number') {\r\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'date') {\r\n <span class=\"tw-text-sm tw-text-gray-600\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'boolean') {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\r\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\r\n </span>\r\n } @else if (column.type === 'status' && column.statusConfig) {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\r\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\r\n </span>\r\n } @else if (column.type === 'actions' && column.actions) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n @for (action of column.actions; track action.key) {\r\n <button\r\n cideEleButton\r\n [variant]=\"action.variant || 'ghost'\"\r\n size=\"xs\"\r\n type=\"button\"\r\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\r\n [title]=\"action.tooltip || action.label\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\r\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\r\n [ngClass]=\"{\r\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\r\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\r\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\r\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\r\n }\">\r\n @if (action.icon) {\r\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\r\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\r\n </svg>\r\n }\r\n {{ action.label }}\r\n </button>\r\n }\r\n </div>\r\n } @else if (column.type === 'custom') {\r\n <!-- Template Renderer -->\r\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\r\n <ng-container \r\n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\r\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\r\n </ng-container>\r\n }\r\n <!-- Default rendering -->\r\n @else {\r\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\r\n }\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Regular cell content (non-tree or non-first column) -->\r\n @if (column.type === 'text') {\r\n <p class=\"tw-text-sm tw-text-gray-900\"\r\n [class.tw-truncate]=\"column.truncate\"\r\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </p>\r\n } @else if (column.type === 'number') {\r\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'date') {\r\n <span class=\"tw-text-sm tw-text-gray-600\">\r\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\r\n </span>\r\n } @else if (column.type === 'boolean') {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\r\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\r\n </span>\r\n } @else if (column.type === 'status' && column.statusConfig) {\r\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\r\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\r\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\r\n </span>\r\n } @else if (column.type === 'actions' && column.actions) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n @for (action of column.actions; track action.key) {\r\n <button\r\n cideEleButton\r\n [variant]=\"action.variant || 'ghost'\"\r\n size=\"xs\"\r\n type=\"button\"\r\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\r\n [title]=\"action.tooltip || action.label\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\r\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\r\n [ngClass]=\"{\r\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\r\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\r\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\r\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\r\n }\">\r\n @if (action.icon) {\r\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\r\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\r\n </svg>\r\n }\r\n {{ action.label }}\r\n </button>\r\n }\r\n </div>\r\n } @else if (column.type === 'custom') {\r\n <!-- Template Renderer -->\r\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\r\n <ng-container \r\n [ngTemplateOutlet]=\"getTemplateRenderer(column.renderer)!\"\r\n [ngTemplateOutletContext]=\"getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\r\n </ng-container>\r\n }\r\n <!-- Default rendering -->\r\n @else {\r\n <div>{{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}</div>\r\n }\r\n }\r\n }\r\n </td>\r\n }\r\n </tr>\r\n }\r\n \r\n <!-- Empty State -->\r\n @if (displayedData.length === 0) {\r\n <tr class=\"tw-h-full\">\r\n <td [attr.colspan]=\"columns.length\" class=\"tw-px-6 tw-py-22 tw-text-center tw-h-full tw-align-middle\">\r\n <div class=\"tw-text-gray-500 tw-flex tw-flex-col tw-items-center tw-justify-center tw-min-h-[300px]\">\r\n <svg class=\"tw-mx-auto tw-h-12 tw-w-12 tw-text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"></path>\r\n </svg>\r\n <h3 class=\"tw-mt-2 tw-text-sm tw-font-medium tw-text-gray-900\">No data found</h3>\r\n <p class=\"tw-mt-1 tw-text-sm tw-text-gray-500\">\r\n @if (searchQuery()) {\r\n No results match your search criteria.\r\n } @else {\r\n There are no items to display.\r\n }\r\n </p>\r\n </div>\r\n </td>\r\n </tr>\r\n }\r\n }\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <!-- Pagination Section -->\r\n @if (paginationConfig.enabled && totalItems() > 0) {\r\n <div class=\"tw-px-3 tw-py-0 tw-border-t tw-border-gray-200 tw-bg-white tw-relative tw-z-20\"\r\n [class.tw-opacity-60]=\"isRefreshing()\">\r\n \r\n <!-- Results Info and Page Size -->\r\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-1 sm:tw-space-y-0\">\r\n \r\n <!-- Results Info -->\r\n @if (paginationConfig.showPageInfo) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\r\n <p class=\"tw-text-sm tw-text-gray-700\">\r\n Showing {{ getItemRangeText() }} results\r\n </p>\r\n \r\n <!-- Page Size Selector -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-500\">view_list</cide-ele-icon>\r\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\r\n <div class=\"tw-w-16 tw-relative\">\r\n <cide-ele-select\r\n [labelHide]=\"true\"\r\n [ngModel]=\"pageSize()\"\r\n (ngModelChange)=\"updatePageSize($event)\"\r\n [options]=\"getPageSizeOptions()\"\r\n [disabled]=\"isRefreshing()\"\r\n fill=\"outline\"\r\n size=\"xs\"\r\n class=\"tw-z-30\">\r\n </cide-ele-select>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Pagination Controls -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n \r\n <!-- Previous/Next and Page Numbers -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\r\n \r\n <!-- First Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"onPageChange(1)\"\r\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"First page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">first_page</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Previous Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"previousPage()\"\r\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Previous page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_left</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Page Numbers -->\r\n @for (page of getEnhancedPageNumbers(); track page) {\r\n @if (page === '...') {\r\n <span class=\"tw-px-2 tw-py-2 tw-text-sm tw-text-gray-500\">...</span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n (click)=\"onPageChange(page)\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-p-0 tw-rounded tw-text-xs tw-font-medium tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n [ngClass]=\"{\r\n 'tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700': currentPage() === page,\r\n 'tw-bg-white tw-text-gray-700 tw-border tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\r\n }\"\r\n [title]=\"'Page ' + page\">\r\n {{ page }}\r\n </button>\r\n }\r\n }\r\n \r\n <!-- Next Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"nextPage()\"\r\n [disabled]=\"!hasNextPage() || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Next page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_right</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Last Page -->\r\n <button\r\n type=\"button\"\r\n (click)=\"onPageChange(totalPages())\"\r\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Last page\">\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">last_page</cide-ele-icon>\r\n </button>\r\n \r\n </div>\r\n\r\n <!-- Quick Jump and Refresh -->\r\n @if (paginationConfig.showQuickJump || paginationConfig.showRefresh) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-1 tw-border-l tw-border-gray-200 tw-pl-2\">\r\n \r\n <!-- Quick Jump -->\r\n @if (paginationConfig.showQuickJump) {\r\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\r\n <span class=\"tw-text-sm tw-text-gray-700\">Go to:</span>\r\n <div class=\"tw-w-16\">\r\n <cide-ele-input id=\"jump-to-page-input\" type=\"number\" [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\"\r\n [(ngModel)]=\"jumpToPage\" [min]=\"1\" [max]=\"totalPages()\"\r\n [disabled]=\"isRefreshing()\"\r\n size=\"xs\"\r\n (keydown.enter)=\"onJumpToPage()\">\r\n </cide-ele-input>\r\n </div>\r\n <button\r\n type=\"button\"\r\n (click)=\"onJumpToPage()\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-p-0 tw-rounded tw-text-xs tw-font-medium tw-bg-gray-100 tw-text-gray-700 tw-border tw-border-gray-300 hover:tw-bg-gray-200 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Go to page\">\r\n Go\r\n </button>\r\n </div>\r\n }\r\n \r\n <!-- Refresh Button -->\r\n @if (paginationConfig.showRefresh) {\r\n <button\r\n type=\"button\"\r\n (click)=\"onRefresh()\"\r\n [disabled]=\"isRefreshing()\"\r\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-7 tw-h-7 tw-p-0 tw-rounded-md tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\r\n title=\"Refresh\">\r\n @if (isRefreshing()) {\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4 tw-animate-spin\">refresh</cide-ele-icon>\r\n } @else {\r\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">refresh</cide-ele-icon>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n \r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".data-grid-container{width:100%;display:flex;flex-direction:column;min-height:400px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:14px;color:#1f2937;background-color:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px #0000000a,0 1px 2px #00000005;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.data-grid-container.tw-h-full{height:100%;min-height:100%}.data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container .tw-overflow-x-auto.tw-flex-1{flex:1;min-height:0}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar{height:6px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af}.data-grid-container table{min-height:300px;border-collapse:separate;border-spacing:0;width:100%;background-color:#fff}.data-grid-container thead{background:linear-gradient(180deg,#fafafa,#f7f7f7);border-bottom:1px solid rgba(0,0,0,.06);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.data-grid-container thead th{background:transparent;color:#6b7280;font-weight:500;font-size:12px;text-transform:none;letter-spacing:-.01em;padding:4px 10px;border-bottom:1px solid rgba(0,0,0,.06);text-align:left;white-space:nowrap;position:relative;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container thead th:first-child{padding-left:12px}.data-grid-container thead th:last-child{padding-right:12px}.data-grid-container thead th:hover{background-color:#00000005}.data-grid-container thead th .column-menu-trigger{opacity:1;transition:all .2s cubic-bezier(.4,0,.2,1);margin-left:4px;cursor:pointer;padding:2px;border-radius:4px}.data-grid-container thead th .column-menu-trigger:hover{background-color:#0000000f}.data-grid-container tbody{background-color:#fff}.data-grid-container tbody td{padding:6px 10px;border-bottom:1px solid rgba(0,0,0,.03);color:#1f2937;font-size:13px;vertical-align:middle;line-height:1.5;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody td:first-child{padding-left:12px}.data-grid-container tbody td:last-child{padding-right:12px}.data-grid-container tbody tr{background-color:#fff;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody tr:hover{background-color:#00000005;transform:scale(1.001)}.data-grid-container tbody tr:hover td{border-bottom-color:#0000000a}.data-grid-container tbody tr:active{background-color:#00000008;transform:scale(.999)}.data-grid-container.tw-h-full table,.data-grid-container.tw-h-full tbody{height:100%}.data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#00000004}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover{background-color:#00000008;transform:scale(1.001)}.data-grid-container.tw-table-sm thead th{padding:8px 10px;font-size:11px}.data-grid-container.tw-table-sm thead th:first-child{padding-left:16px}.data-grid-container.tw-table-sm thead th:last-child{padding-right:16px}.data-grid-container.tw-table-sm tbody td{padding:8px 10px;font-size:12px;line-height:1.4}.data-grid-container.tw-table-sm tbody td:first-child{padding-left:16px}.data-grid-container.tw-table-sm tbody td:last-child{padding-right:16px}.data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:#e5f3ff}.data-grid-container.loading-overlay{position:relative}.data-grid-container.loading-overlay:after{content:\"\";position:absolute;inset:0;background:#ffffffd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:10;animation:fadeIn .2s cubic-bezier(.4,0,.2,1)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.data-grid-container .tw-animate-pulse div{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.data-grid-container .data-grid-action-buttons button{position:relative;border:1px solid rgba(0,0,0,.08);background:transparent;outline:none;cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:6px;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-action-buttons button cide-ele-icon{color:#6b7280;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-action-buttons button:hover:not(:disabled){background-color:#0000000a;border-color:#0000001f}.data-grid-container .data-grid-action-buttons button:hover:not(:disabled) cide-ele-icon{color:#374151;transform:scale(1.05)}.data-grid-container .data-grid-action-buttons button:active:not(:disabled){background-color:#00000014;border-color:#00000026;transform:scale(.95)}.data-grid-container .data-grid-action-buttons button:active:not(:disabled) cide-ele-icon{transform:scale(.98)}.data-grid-container .data-grid-action-buttons button:focus-visible{outline:none;border-color:#3b82f64d;box-shadow:0 0 0 3px #3b82f61f}.data-grid-container .data-grid-action-buttons button:disabled{cursor:not-allowed;opacity:.3;background:transparent!important;border-color:#0000000d}.data-grid-container .action-buttons{display:flex;gap:.25rem}.data-grid-container .action-buttons button{transition:all .15s ease-in-out}.data-grid-container .action-buttons button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.data-grid-container .action-buttons button:disabled{cursor:not-allowed;opacity:.5}.data-grid-container .pagination-controls{padding:12px 20px;border-top:1px solid rgba(0,0,0,.06);background:linear-gradient(180deg,#fafafa,#f7f7f7)}.data-grid-container .pagination-controls button{transition:all .2s cubic-bezier(.4,0,.2,1);border-radius:6px}.data-grid-container .pagination-controls button:hover:not(:disabled){background-color:#0000000a;transform:scale(1.02)}.data-grid-container .pagination-controls button:active:not(:disabled){transform:scale(.98);background-color:#00000014}.data-grid-container .pagination-controls button:disabled{cursor:not-allowed;opacity:.3}.data-grid-container .pagination-controls button.active{background-color:#3b82f61a;color:#3b82f6;font-weight:500;box-shadow:0 0 0 1px #3b82f633}.data-grid-container .pagination-controls input[type=number]{transition:all .2s cubic-bezier(.4,0,.2,1);border:1px solid rgba(0,0,0,.06);border-radius:6px}.data-grid-container .pagination-controls input[type=number]:hover{border-color:#0000001a}.data-grid-container .pagination-controls input[type=number]:focus{outline:none;border-color:#3b82f64d;box-shadow:0 0 0 3px #3b82f614;background-color:#fff}.data-grid-container .status-badge{font-weight:500;letter-spacing:-.01em;padding:4px 12px;border-radius:12px;font-size:12px;display:inline-flex;align-items:center;transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .status-badge.active{background-color:#34d39926;color:#059669;border:1px solid rgba(52,211,153,.3)}.data-grid-container .status-badge.active:hover{background-color:#34d39933}.data-grid-container .status-badge.inactive{background-color:#f8717126;color:#dc2626;border:1px solid rgba(248,113,113,.3)}.data-grid-container .status-badge.inactive:hover{background-color:#f8717133}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper{transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper:hover:not(:has(input:disabled)){background-color:#00000005}.data-grid-container .data-grid-search-input ::ng-deep input{font-size:13px;font-weight:400;color:#1f2937;transition:all .2s cubic-bezier(.4,0,.2,1);border-radius:8px}.data-grid-container .data-grid-search-input ::ng-deep input::placeholder{color:#9ca3af;font-weight:400}.data-grid-container .data-grid-search-input ::ng-deep input:focus{outline:none;border-color:#3b82f64d;box-shadow:0 0 0 3px #3b82f614;background-color:#fff}.data-grid-container .data-grid-search-input ::ng-deep input:disabled{background-color:#00000005;color:#9ca3af;cursor:not-allowed}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-leading-icon cide-ele-icon{color:#9ca3af;transition:color .2s cubic-bezier(.4,0,.2,1)}.data-grid-container .data-grid-search-input ::ng-deep input:focus~.cide-input-leading-icon cide-ele-icon,.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper:has(input:focus) .cide-input-leading-icon cide-ele-icon{color:#6b7280}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper[data-fill=outline]{border:1px solid #e5e7eb;background-color:#fafafa;border-radius:8px}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper[data-fill=outline]:hover:not(:has(input:disabled)){border-color:#d1d5db;background-color:#fff}.data-grid-container .data-grid-search-input ::ng-deep .cide-input-wrapper[data-fill=outline]:has(input:focus){border-color:#3b82f64d;background-color:#fff}.data-grid-container .search-input{position:relative}.data-grid-container .search-input input{transition:all .15s ease-in-out}.data-grid-container .search-input input:focus{box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .search-input .search-icon{pointer-events:none}.data-grid-container .column-menu-dropdown{animation:dropdownFadeIn .15s cubic-bezier(.4,0,.2,1);box-shadow:0 10px 25px #0000001a,0 4px 10px #0000000d;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(0,0,0,.05)}.data-grid-container .column-menu-dropdown button{font-size:13px;font-weight:400;letter-spacing:-.01em;text-align:left;transition:all .15s cubic-bezier(.4,0,.2,1)}.data-grid-container .column-menu-dropdown button:hover{background-color:#3b82f60d;color:#1f2937}.data-grid-container .column-menu-dropdown button:hover cide-ele-icon{color:#3b82f6}.data-grid-container .column-menu-dropdown button:active{background-color:#3b82f61a;transform:scale(.98)}.data-grid-container .column-menu-dropdown .tw-uppercase{letter-spacing:.05em}@keyframes dropdownFadeIn{0%{opacity:0;transform:translateY(-8px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.data-grid-container .empty-state{padding:4rem 2rem;text-align:center}.data-grid-container .empty-state svg{margin:0 auto 1.5rem;opacity:.3;transition:all .3s cubic-bezier(.4,0,.2,1)}.data-grid-container .empty-state svg:hover{opacity:.5;transform:scale(1.05)}.data-grid-container .empty-state h3{margin-bottom:.75rem;font-weight:600;color:#374151;font-size:16px;letter-spacing:-.01em}.data-grid-container .empty-state p{color:#6b7280;font-size:14px;line-height:1.5}.data-grid-container.tw-h-full tbody tr:only-child td{height:100%;vertical-align:middle}.data-grid-container.tw-h-full table.empty-table{height:100%}.data-grid-container.tw-h-full tbody tr:only-child{height:100%}@media (max-width: 640px){.data-grid-container .tw-px-6{padding-left:1rem;padding-right:1rem}.data-grid-container .pagination-controls{flex-direction:column;gap:1rem}.data-grid-container .pagination-controls .flex{justify-content:center}.data-grid-container .pagination-info{display:none}.data-grid-container .search-actions{flex-direction:column;gap:1rem}}@media (prefers-color-scheme: dark){.data-grid-container.dark-mode{background-color:#1f2937;color:#f9fafb}.data-grid-container.dark-mode table{background-color:#1f2937}.data-grid-container.dark-mode thead{background-color:#374151}.data-grid-container.dark-mode tbody tr{border-color:#374151}.data-grid-container.dark-mode tbody tr:hover{background-color:#374151}.data-grid-container.dark-mode .tw-text-gray-900{color:#f9fafb!important}.data-grid-container.dark-mode .tw-text-gray-600{color:#d1d5db!important}.data-grid-container.dark-mode .tw-text-gray-500{color:#9ca3af!important}}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:#f3f4f6}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed #3b82f6;border-radius:4px}.data-grid-container.drag-drop-enabled tbody tr.tw-bg-blue-50{background-color:#eff6ffe6}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]{cursor:move;position:relative}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover{background-color:#f8fafc;box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:active{cursor:grabbing}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]{position:relative}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#60a5fa);z-index:10}.data-grid-container.tree-enabled tbody tr td:first-child{position:relative}.data-grid-container.tree-enabled tbody tr td:first-child button{transition:all .15s ease}.data-grid-container.tree-enabled tbody tr td:first-child button:hover{background-color:#f3f4f6;border-radius:2px}.data-grid-container.tree-enabled tbody tr td:first-child button svg{transition:transform .2s ease}.data-grid-container.tree-enabled tbody tr[style*=padding-left]{border-left:2px solid transparent}.data-grid-container.tree-enabled tbody tr[style*=padding-left]:hover{border-left-color:#e5e7eb}.tree-level-0{border-bottom:2px solid #e5e7eb!important;background-color:#fff}.tree-level-0:hover{background-color:#f5f5f5!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid #d1d5db!important;background-color:#fafafa}.tree-level-1:hover{background-color:#f1f5f9!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid #d1d5db!important;background-color:#f9fafb}.tree-level-2:hover{background-color:#f3f4f6!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid #e5e7eb!important;background-color:#fefefe}.tree-level-deep:hover{background-color:#f9fafb!important}.tree-level-deep td:first-child{font-weight:300}table td{box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}table td.tw-overflow-hidden{overflow:hidden;text-overflow:ellipsis}table td:first-child{min-width:150px}table td:first-child>div{display:flex;align-items:center;min-width:0}table td:first-child>div .tw-flex{min-width:0}table td .tw-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}\n"] }]
|
|
9300
10075
|
}], propDecorators: { config: [{
|
|
9301
10076
|
type: Input
|
|
9302
10077
|
}], templateRenderers: [{
|
|
@@ -9645,170 +10420,170 @@ class CideEleDropdownComponent {
|
|
|
9645
10420
|
this.dropdownManager.unregisterDropdown(this.dropdownId);
|
|
9646
10421
|
}
|
|
9647
10422
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9648
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleDropdownComponent, isStandalone: true, selector: "cide-ele-dropdown", inputs: { items: "items", config: "config", triggerTemplate: "triggerTemplate", menuTemplate: "menuTemplate" }, outputs: { itemClick: "itemClick", dropdownToggle: "dropdownToggle" }, host: { listeners: { "window:resize": "onWindowResize()", "window:scroll": "onWindowScroll()" } }, viewQueries: [{ propertyName: "dropdownContainer", first: true, predicate: ["dropdownContainer"], descendants: true, isSignal: true }, { propertyName: "dropdownMenu", first: true, predicate: ["dropdownMenu"], descendants: true, isSignal: true }, { propertyName: "dropdownMenuTemplate", first: true, predicate: ["dropdownMenuTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
9649
|
-
<div class="tw-relative" #dropdownContainer>
|
|
9650
|
-
<!-- Trigger Button -->
|
|
9651
|
-
<button
|
|
9652
|
-
type="button"
|
|
9653
|
-
class="dropdown-trigger tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-rounded-full tw-text-gray-400 hover:tw-text-gray-600 hover:tw-bg-gray-100 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-offset-2 focus:tw-ring-blue-500 tw-transition-all tw-duration-150"
|
|
9654
|
-
[class]="getTriggerClasses()"
|
|
9655
|
-
(click)="toggleDropdown($event)"
|
|
9656
|
-
[title]="config.triggerIcon === 'more_vert' ? 'More actions' : 'Options'">
|
|
9657
|
-
|
|
9658
|
-
<!-- Custom Trigger Template -->
|
|
9659
|
-
@if (triggerTemplate) {
|
|
9660
|
-
<ng-container *ngTemplateOutlet="triggerTemplate; context: { $implicit: isOpen() }">
|
|
9661
|
-
</ng-container>
|
|
9662
|
-
} @else {
|
|
9663
|
-
<!-- Default Trigger -->
|
|
9664
|
-
<cide-ele-icon
|
|
9665
|
-
[class]="getTriggerIconClasses()"
|
|
9666
|
-
[size]="config.triggerSize || 'sm'">
|
|
9667
|
-
{{ config.triggerIcon || 'more_vert' }}
|
|
9668
|
-
</cide-ele-icon>
|
|
9669
|
-
}
|
|
9670
|
-
</button>
|
|
9671
|
-
|
|
9672
|
-
<!-- Dropdown Menu (Non-Portal) -->
|
|
9673
|
-
@if (isOpen() && !getSafeConfig().usePortal) {
|
|
9674
|
-
<div
|
|
9675
|
-
#dropdownMenu
|
|
9676
|
-
class="dropdown-menu tw-absolute tw-z-50 tw-bg-white tw-shadow-lg tw-border tw-border-gray-200 tw-rounded-md tw-text-black"
|
|
9677
|
-
[class]="getMenuClasses()"
|
|
9678
|
-
[style]="getMenuStyles()"
|
|
9679
|
-
role="menu">
|
|
9680
|
-
<ng-container *ngTemplateOutlet="dropdownMenuTemplate; context: { $implicit: items }">
|
|
9681
|
-
</ng-container>
|
|
9682
|
-
</div>
|
|
9683
|
-
}
|
|
9684
|
-
</div>
|
|
9685
|
-
|
|
9686
|
-
<!-- Dropdown Menu Template (for both portal and non-portal) -->
|
|
9687
|
-
<ng-template #dropdownMenuTemplate let-context="context">
|
|
9688
|
-
<!-- Custom Menu Template -->
|
|
9689
|
-
@if (menuTemplate) {
|
|
9690
|
-
<ng-container *ngTemplateOutlet="menuTemplate; context: { $implicit: context?.items }">
|
|
9691
|
-
</ng-container>
|
|
9692
|
-
} @else {
|
|
9693
|
-
<!-- Default Menu -->
|
|
9694
|
-
<div class="tw-py-1" role="none">
|
|
9695
|
-
@for (item of context?.items; track item.id) {
|
|
9696
|
-
@if (item.divider) {
|
|
9697
|
-
<!-- Divider Item -->
|
|
9698
|
-
<hr class="tw-border-t tw-border-gray-200 tw-my-1" role="separator">
|
|
9699
|
-
} @else {
|
|
9700
|
-
<!-- Regular Menu Item -->
|
|
9701
|
-
<button
|
|
9702
|
-
type="button"
|
|
9703
|
-
[class]="getItemClasses(item)"
|
|
9704
|
-
[disabled]="item.disabled"
|
|
9705
|
-
(click)="onItemClick(item, $event)"
|
|
9706
|
-
[attr.data-item-id]="item.id">
|
|
9707
|
-
|
|
9708
|
-
<!-- Item Icon -->
|
|
9709
|
-
@if (item.icon) {
|
|
9710
|
-
<cide-ele-icon
|
|
9711
|
-
class="tw-w-4 tw-h-4 tw-mr-2"
|
|
9712
|
-
[class]="item.iconColor || 'tw-text-gray-400'"
|
|
9713
|
-
size="xs">
|
|
9714
|
-
{{ item.icon }}
|
|
9715
|
-
</cide-ele-icon>
|
|
9716
|
-
}
|
|
9717
|
-
|
|
9718
|
-
<!-- Item Label -->
|
|
9719
|
-
<span [class]="item.textColor || 'tw-text-gray-700'">
|
|
9720
|
-
{{ item.label }}
|
|
9721
|
-
</span>
|
|
9722
|
-
</button>
|
|
9723
|
-
}
|
|
9724
|
-
}
|
|
9725
|
-
</div>
|
|
9726
|
-
}
|
|
9727
|
-
</ng-template>
|
|
10423
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleDropdownComponent, isStandalone: true, selector: "cide-ele-dropdown", inputs: { items: "items", config: "config", triggerTemplate: "triggerTemplate", menuTemplate: "menuTemplate" }, outputs: { itemClick: "itemClick", dropdownToggle: "dropdownToggle" }, host: { listeners: { "window:resize": "onWindowResize()", "window:scroll": "onWindowScroll()" } }, viewQueries: [{ propertyName: "dropdownContainer", first: true, predicate: ["dropdownContainer"], descendants: true, isSignal: true }, { propertyName: "dropdownMenu", first: true, predicate: ["dropdownMenu"], descendants: true, isSignal: true }, { propertyName: "dropdownMenuTemplate", first: true, predicate: ["dropdownMenuTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
10424
|
+
<div class="tw-relative" #dropdownContainer>
|
|
10425
|
+
<!-- Trigger Button -->
|
|
10426
|
+
<button
|
|
10427
|
+
type="button"
|
|
10428
|
+
class="dropdown-trigger tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-rounded-full tw-text-gray-400 hover:tw-text-gray-600 hover:tw-bg-gray-100 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-offset-2 focus:tw-ring-blue-500 tw-transition-all tw-duration-150"
|
|
10429
|
+
[class]="getTriggerClasses()"
|
|
10430
|
+
(click)="toggleDropdown($event)"
|
|
10431
|
+
[title]="config.triggerIcon === 'more_vert' ? 'More actions' : 'Options'">
|
|
10432
|
+
|
|
10433
|
+
<!-- Custom Trigger Template -->
|
|
10434
|
+
@if (triggerTemplate) {
|
|
10435
|
+
<ng-container *ngTemplateOutlet="triggerTemplate; context: { $implicit: isOpen() }">
|
|
10436
|
+
</ng-container>
|
|
10437
|
+
} @else {
|
|
10438
|
+
<!-- Default Trigger -->
|
|
10439
|
+
<cide-ele-icon
|
|
10440
|
+
[class]="getTriggerIconClasses()"
|
|
10441
|
+
[size]="config.triggerSize || 'sm'">
|
|
10442
|
+
{{ config.triggerIcon || 'more_vert' }}
|
|
10443
|
+
</cide-ele-icon>
|
|
10444
|
+
}
|
|
10445
|
+
</button>
|
|
10446
|
+
|
|
10447
|
+
<!-- Dropdown Menu (Non-Portal) -->
|
|
10448
|
+
@if (isOpen() && !getSafeConfig().usePortal) {
|
|
10449
|
+
<div
|
|
10450
|
+
#dropdownMenu
|
|
10451
|
+
class="dropdown-menu tw-absolute tw-z-50 tw-bg-white tw-shadow-lg tw-border tw-border-gray-200 tw-rounded-md tw-text-black"
|
|
10452
|
+
[class]="getMenuClasses()"
|
|
10453
|
+
[style]="getMenuStyles()"
|
|
10454
|
+
role="menu">
|
|
10455
|
+
<ng-container *ngTemplateOutlet="dropdownMenuTemplate; context: { $implicit: items }">
|
|
10456
|
+
</ng-container>
|
|
10457
|
+
</div>
|
|
10458
|
+
}
|
|
10459
|
+
</div>
|
|
10460
|
+
|
|
10461
|
+
<!-- Dropdown Menu Template (for both portal and non-portal) -->
|
|
10462
|
+
<ng-template #dropdownMenuTemplate let-context="context">
|
|
10463
|
+
<!-- Custom Menu Template -->
|
|
10464
|
+
@if (menuTemplate) {
|
|
10465
|
+
<ng-container *ngTemplateOutlet="menuTemplate; context: { $implicit: context?.items }">
|
|
10466
|
+
</ng-container>
|
|
10467
|
+
} @else {
|
|
10468
|
+
<!-- Default Menu -->
|
|
10469
|
+
<div class="tw-py-1" role="none">
|
|
10470
|
+
@for (item of context?.items; track item.id) {
|
|
10471
|
+
@if (item.divider) {
|
|
10472
|
+
<!-- Divider Item -->
|
|
10473
|
+
<hr class="tw-border-t tw-border-gray-200 tw-my-1" role="separator">
|
|
10474
|
+
} @else {
|
|
10475
|
+
<!-- Regular Menu Item -->
|
|
10476
|
+
<button
|
|
10477
|
+
type="button"
|
|
10478
|
+
[class]="getItemClasses(item)"
|
|
10479
|
+
[disabled]="item.disabled"
|
|
10480
|
+
(click)="onItemClick(item, $event)"
|
|
10481
|
+
[attr.data-item-id]="item.id">
|
|
10482
|
+
|
|
10483
|
+
<!-- Item Icon -->
|
|
10484
|
+
@if (item.icon) {
|
|
10485
|
+
<cide-ele-icon
|
|
10486
|
+
class="tw-w-4 tw-h-4 tw-mr-2"
|
|
10487
|
+
[class]="item.iconColor || 'tw-text-gray-400'"
|
|
10488
|
+
size="xs">
|
|
10489
|
+
{{ item.icon }}
|
|
10490
|
+
</cide-ele-icon>
|
|
10491
|
+
}
|
|
10492
|
+
|
|
10493
|
+
<!-- Item Label -->
|
|
10494
|
+
<span [class]="item.textColor || 'tw-text-gray-700'">
|
|
10495
|
+
{{ item.label }}
|
|
10496
|
+
</span>
|
|
10497
|
+
</button>
|
|
10498
|
+
}
|
|
10499
|
+
}
|
|
10500
|
+
</div>
|
|
10501
|
+
}
|
|
10502
|
+
</ng-template>
|
|
9728
10503
|
`, isInline: true, styles: [".dropdown-menu{transform-origin:top right}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
9729
10504
|
}
|
|
9730
10505
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDropdownComponent, decorators: [{
|
|
9731
10506
|
type: Component,
|
|
9732
|
-
args: [{ selector: 'cide-ele-dropdown', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
9733
|
-
<div class="tw-relative" #dropdownContainer>
|
|
9734
|
-
<!-- Trigger Button -->
|
|
9735
|
-
<button
|
|
9736
|
-
type="button"
|
|
9737
|
-
class="dropdown-trigger tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-rounded-full tw-text-gray-400 hover:tw-text-gray-600 hover:tw-bg-gray-100 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-offset-2 focus:tw-ring-blue-500 tw-transition-all tw-duration-150"
|
|
9738
|
-
[class]="getTriggerClasses()"
|
|
9739
|
-
(click)="toggleDropdown($event)"
|
|
9740
|
-
[title]="config.triggerIcon === 'more_vert' ? 'More actions' : 'Options'">
|
|
9741
|
-
|
|
9742
|
-
<!-- Custom Trigger Template -->
|
|
9743
|
-
@if (triggerTemplate) {
|
|
9744
|
-
<ng-container *ngTemplateOutlet="triggerTemplate; context: { $implicit: isOpen() }">
|
|
9745
|
-
</ng-container>
|
|
9746
|
-
} @else {
|
|
9747
|
-
<!-- Default Trigger -->
|
|
9748
|
-
<cide-ele-icon
|
|
9749
|
-
[class]="getTriggerIconClasses()"
|
|
9750
|
-
[size]="config.triggerSize || 'sm'">
|
|
9751
|
-
{{ config.triggerIcon || 'more_vert' }}
|
|
9752
|
-
</cide-ele-icon>
|
|
9753
|
-
}
|
|
9754
|
-
</button>
|
|
9755
|
-
|
|
9756
|
-
<!-- Dropdown Menu (Non-Portal) -->
|
|
9757
|
-
@if (isOpen() && !getSafeConfig().usePortal) {
|
|
9758
|
-
<div
|
|
9759
|
-
#dropdownMenu
|
|
9760
|
-
class="dropdown-menu tw-absolute tw-z-50 tw-bg-white tw-shadow-lg tw-border tw-border-gray-200 tw-rounded-md tw-text-black"
|
|
9761
|
-
[class]="getMenuClasses()"
|
|
9762
|
-
[style]="getMenuStyles()"
|
|
9763
|
-
role="menu">
|
|
9764
|
-
<ng-container *ngTemplateOutlet="dropdownMenuTemplate; context: { $implicit: items }">
|
|
9765
|
-
</ng-container>
|
|
9766
|
-
</div>
|
|
9767
|
-
}
|
|
9768
|
-
</div>
|
|
9769
|
-
|
|
9770
|
-
<!-- Dropdown Menu Template (for both portal and non-portal) -->
|
|
9771
|
-
<ng-template #dropdownMenuTemplate let-context="context">
|
|
9772
|
-
<!-- Custom Menu Template -->
|
|
9773
|
-
@if (menuTemplate) {
|
|
9774
|
-
<ng-container *ngTemplateOutlet="menuTemplate; context: { $implicit: context?.items }">
|
|
9775
|
-
</ng-container>
|
|
9776
|
-
} @else {
|
|
9777
|
-
<!-- Default Menu -->
|
|
9778
|
-
<div class="tw-py-1" role="none">
|
|
9779
|
-
@for (item of context?.items; track item.id) {
|
|
9780
|
-
@if (item.divider) {
|
|
9781
|
-
<!-- Divider Item -->
|
|
9782
|
-
<hr class="tw-border-t tw-border-gray-200 tw-my-1" role="separator">
|
|
9783
|
-
} @else {
|
|
9784
|
-
<!-- Regular Menu Item -->
|
|
9785
|
-
<button
|
|
9786
|
-
type="button"
|
|
9787
|
-
[class]="getItemClasses(item)"
|
|
9788
|
-
[disabled]="item.disabled"
|
|
9789
|
-
(click)="onItemClick(item, $event)"
|
|
9790
|
-
[attr.data-item-id]="item.id">
|
|
9791
|
-
|
|
9792
|
-
<!-- Item Icon -->
|
|
9793
|
-
@if (item.icon) {
|
|
9794
|
-
<cide-ele-icon
|
|
9795
|
-
class="tw-w-4 tw-h-4 tw-mr-2"
|
|
9796
|
-
[class]="item.iconColor || 'tw-text-gray-400'"
|
|
9797
|
-
size="xs">
|
|
9798
|
-
{{ item.icon }}
|
|
9799
|
-
</cide-ele-icon>
|
|
9800
|
-
}
|
|
9801
|
-
|
|
9802
|
-
<!-- Item Label -->
|
|
9803
|
-
<span [class]="item.textColor || 'tw-text-gray-700'">
|
|
9804
|
-
{{ item.label }}
|
|
9805
|
-
</span>
|
|
9806
|
-
</button>
|
|
9807
|
-
}
|
|
9808
|
-
}
|
|
9809
|
-
</div>
|
|
9810
|
-
}
|
|
9811
|
-
</ng-template>
|
|
10507
|
+
args: [{ selector: 'cide-ele-dropdown', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
10508
|
+
<div class="tw-relative" #dropdownContainer>
|
|
10509
|
+
<!-- Trigger Button -->
|
|
10510
|
+
<button
|
|
10511
|
+
type="button"
|
|
10512
|
+
class="dropdown-trigger tw-inline-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-rounded-full tw-text-gray-400 hover:tw-text-gray-600 hover:tw-bg-gray-100 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-offset-2 focus:tw-ring-blue-500 tw-transition-all tw-duration-150"
|
|
10513
|
+
[class]="getTriggerClasses()"
|
|
10514
|
+
(click)="toggleDropdown($event)"
|
|
10515
|
+
[title]="config.triggerIcon === 'more_vert' ? 'More actions' : 'Options'">
|
|
10516
|
+
|
|
10517
|
+
<!-- Custom Trigger Template -->
|
|
10518
|
+
@if (triggerTemplate) {
|
|
10519
|
+
<ng-container *ngTemplateOutlet="triggerTemplate; context: { $implicit: isOpen() }">
|
|
10520
|
+
</ng-container>
|
|
10521
|
+
} @else {
|
|
10522
|
+
<!-- Default Trigger -->
|
|
10523
|
+
<cide-ele-icon
|
|
10524
|
+
[class]="getTriggerIconClasses()"
|
|
10525
|
+
[size]="config.triggerSize || 'sm'">
|
|
10526
|
+
{{ config.triggerIcon || 'more_vert' }}
|
|
10527
|
+
</cide-ele-icon>
|
|
10528
|
+
}
|
|
10529
|
+
</button>
|
|
10530
|
+
|
|
10531
|
+
<!-- Dropdown Menu (Non-Portal) -->
|
|
10532
|
+
@if (isOpen() && !getSafeConfig().usePortal) {
|
|
10533
|
+
<div
|
|
10534
|
+
#dropdownMenu
|
|
10535
|
+
class="dropdown-menu tw-absolute tw-z-50 tw-bg-white tw-shadow-lg tw-border tw-border-gray-200 tw-rounded-md tw-text-black"
|
|
10536
|
+
[class]="getMenuClasses()"
|
|
10537
|
+
[style]="getMenuStyles()"
|
|
10538
|
+
role="menu">
|
|
10539
|
+
<ng-container *ngTemplateOutlet="dropdownMenuTemplate; context: { $implicit: items }">
|
|
10540
|
+
</ng-container>
|
|
10541
|
+
</div>
|
|
10542
|
+
}
|
|
10543
|
+
</div>
|
|
10544
|
+
|
|
10545
|
+
<!-- Dropdown Menu Template (for both portal and non-portal) -->
|
|
10546
|
+
<ng-template #dropdownMenuTemplate let-context="context">
|
|
10547
|
+
<!-- Custom Menu Template -->
|
|
10548
|
+
@if (menuTemplate) {
|
|
10549
|
+
<ng-container *ngTemplateOutlet="menuTemplate; context: { $implicit: context?.items }">
|
|
10550
|
+
</ng-container>
|
|
10551
|
+
} @else {
|
|
10552
|
+
<!-- Default Menu -->
|
|
10553
|
+
<div class="tw-py-1" role="none">
|
|
10554
|
+
@for (item of context?.items; track item.id) {
|
|
10555
|
+
@if (item.divider) {
|
|
10556
|
+
<!-- Divider Item -->
|
|
10557
|
+
<hr class="tw-border-t tw-border-gray-200 tw-my-1" role="separator">
|
|
10558
|
+
} @else {
|
|
10559
|
+
<!-- Regular Menu Item -->
|
|
10560
|
+
<button
|
|
10561
|
+
type="button"
|
|
10562
|
+
[class]="getItemClasses(item)"
|
|
10563
|
+
[disabled]="item.disabled"
|
|
10564
|
+
(click)="onItemClick(item, $event)"
|
|
10565
|
+
[attr.data-item-id]="item.id">
|
|
10566
|
+
|
|
10567
|
+
<!-- Item Icon -->
|
|
10568
|
+
@if (item.icon) {
|
|
10569
|
+
<cide-ele-icon
|
|
10570
|
+
class="tw-w-4 tw-h-4 tw-mr-2"
|
|
10571
|
+
[class]="item.iconColor || 'tw-text-gray-400'"
|
|
10572
|
+
size="xs">
|
|
10573
|
+
{{ item.icon }}
|
|
10574
|
+
</cide-ele-icon>
|
|
10575
|
+
}
|
|
10576
|
+
|
|
10577
|
+
<!-- Item Label -->
|
|
10578
|
+
<span [class]="item.textColor || 'tw-text-gray-700'">
|
|
10579
|
+
{{ item.label }}
|
|
10580
|
+
</span>
|
|
10581
|
+
</button>
|
|
10582
|
+
}
|
|
10583
|
+
}
|
|
10584
|
+
</div>
|
|
10585
|
+
}
|
|
10586
|
+
</ng-template>
|
|
9812
10587
|
`, styles: [".dropdown-menu{transform-origin:top right}\n"] }]
|
|
9813
10588
|
}], propDecorators: { items: [{
|
|
9814
10589
|
type: Input
|
|
@@ -10006,222 +10781,222 @@ class CideEleFloatingContainerComponent {
|
|
|
10006
10781
|
return 'linear-gradient(135deg, var(--cide-theme-primary-color) 0%, var(--tw-blue-400) 100%)';
|
|
10007
10782
|
}
|
|
10008
10783
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10009
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingContainerComponent, isStandalone: true, selector: "cide-ele-floating-container", inputs: { config: "config", isMinimized: "isMinimized", isMaximized: "isMaximized", isVisible: "isVisible", zIndex: "zIndex" }, outputs: { closeEvent: "closeEvent", minimizeEvent: "minimizeEvent", maximizeEvent: "maximizeEvent", clickEvent: "clickEvent" }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: `
|
|
10010
|
-
<div
|
|
10011
|
-
#container
|
|
10012
|
-
class="tw-fixed tw-bg-gray-50 tw-bg-opacity-90 tw-backdrop-blur-sm tw-border tw-border-gray-200 tw-border-opacity-50 tw-rounded-xl tw-shadow-lg tw-transition-all tw-duration-200 tw-flex tw-flex-col"
|
|
10013
|
-
[style.left.px]="isMaximized() ? 20 : position().x"
|
|
10014
|
-
[style.top.px]="isMaximized() ? 20 : position().y"
|
|
10015
|
-
[style.width]="isMaximized() ? 'calc(100vw - 40px)' : (config().width || '400px')"
|
|
10016
|
-
[style.height]="isMaximized() ? 'calc(100vh - 40px)' : (isMinimized() ? 'auto' : (savedHeight || config().height || '500px'))"
|
|
10017
|
-
[style.min-width]="config().minWidth || '300px'"
|
|
10018
|
-
[style.min-height]="isMinimized() ? 'auto' : (config().minHeight || '200px')"
|
|
10019
|
-
[style.max-width]="config().maxWidth || '90vw'"
|
|
10020
|
-
[style.max-height]="config().maxHeight || '90vh'"
|
|
10021
|
-
[style.z-index]="zIndex()"
|
|
10022
|
-
[class.tw-cursor-move]="isDragging()"
|
|
10023
|
-
[class.tw-select-none]="true"
|
|
10024
|
-
(mousedown)="onMouseDown($event)"
|
|
10025
|
-
(mousemove)="onMouseMove($event)"
|
|
10026
|
-
(mouseup)="onMouseUp($event)"
|
|
10027
|
-
(mouseleave)="onMouseUp($event)">
|
|
10028
|
-
|
|
10029
|
-
<!-- Header -->
|
|
10030
|
-
<div
|
|
10031
|
-
class="tw-flex tw-items-center tw-justify-between tw-px-3 tw-py-2 tw-border-b tw-border-gray-200 tw-border-opacity-30 tw-rounded-t-xl tw-cursor-move tw-transition-all tw-duration-200 tw-shadow-sm tw-flex-shrink-0"
|
|
10032
|
-
[style.background]="getHeaderBackground()"
|
|
10033
|
-
(mousedown)="startDrag($event)"
|
|
10034
|
-
(click)="onHeaderClick($event)">
|
|
10035
|
-
|
|
10036
|
-
<!-- Title Section -->
|
|
10037
|
-
<div class="tw-flex tw-items-center tw-space-x-2 tw-text-white">
|
|
10038
|
-
@if (config().icon) {
|
|
10039
|
-
<cide-ele-icon class="tw-w-4 tw-h-4">{{ config().icon }}</cide-ele-icon>
|
|
10040
|
-
}
|
|
10041
|
-
<h2 class="tw-text-sm tw-font-medium tw-truncate">{{ config().title }}</h2>
|
|
10042
|
-
@if (isMinimized()) {
|
|
10043
|
-
<span class="tw-text-xs tw-opacity-75">(Click to restore)</span>
|
|
10044
|
-
}
|
|
10045
|
-
</div>
|
|
10046
|
-
|
|
10047
|
-
<!-- Action Buttons -->
|
|
10048
|
-
<div class="tw-flex tw-items-center tw-space-x-1">
|
|
10049
|
-
@if (config().minimizable !== false) {
|
|
10050
|
-
<button
|
|
10051
|
-
cideEleButton
|
|
10052
|
-
variant="ghost"
|
|
10053
|
-
size="xs"
|
|
10054
|
-
type="button"
|
|
10055
|
-
(click)="minimize(); $event.stopPropagation()"
|
|
10056
|
-
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10057
|
-
[title]="isMinimized() ? 'Restore' : 'Minimize'">
|
|
10058
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMinimized() ? 'open_in_full' : 'remove' }}</cide-ele-icon>
|
|
10059
|
-
</button>
|
|
10060
|
-
}
|
|
10061
|
-
@if (config().maximizable !== false) {
|
|
10062
|
-
<button
|
|
10063
|
-
cideEleButton
|
|
10064
|
-
variant="ghost"
|
|
10065
|
-
size="xs"
|
|
10066
|
-
type="button"
|
|
10067
|
-
(click)="toggleMaximize(); $event.stopPropagation()"
|
|
10068
|
-
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10069
|
-
[title]="isMaximized() ? 'Restore' : 'Maximize'">
|
|
10070
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMaximized() ? 'fullscreen_exit' : 'fullscreen' }}</cide-ele-icon>
|
|
10071
|
-
</button>
|
|
10072
|
-
}
|
|
10073
|
-
@if (config().closable !== false) {
|
|
10074
|
-
<button
|
|
10075
|
-
cideEleButton
|
|
10076
|
-
variant="ghost"
|
|
10077
|
-
size="xs"
|
|
10078
|
-
type="button"
|
|
10079
|
-
(click)="close(); $event.stopPropagation()"
|
|
10080
|
-
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10081
|
-
title="Close">
|
|
10082
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">close</cide-ele-icon>
|
|
10083
|
-
</button>
|
|
10084
|
-
}
|
|
10085
|
-
</div>
|
|
10086
|
-
</div>
|
|
10087
|
-
|
|
10088
|
-
<!-- Content with @defer for performance -->
|
|
10089
|
-
@if (!isMinimized()) {
|
|
10090
|
-
<div
|
|
10091
|
-
class="tw-flex-1 tw-overflow-auto tw-overflow-x-hidden floating-container-content tw-bg-gray-50 tw-bg-opacity-90"
|
|
10092
|
-
[style.height]="isMaximized() ? 'calc(100vh - 100px)' : 'auto'"
|
|
10093
|
-
[style.min-height]="'200px'">
|
|
10094
|
-
@defer (when !isMinimized() && isVisible()) {
|
|
10095
|
-
<div class="tw-p-0 tw-min-h-full">
|
|
10096
|
-
<ng-content></ng-content>
|
|
10097
|
-
</div>
|
|
10098
|
-
} @placeholder {
|
|
10099
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4 tw-min-h-[200px]">
|
|
10100
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10101
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading content...</span>
|
|
10102
|
-
</div>
|
|
10103
|
-
}
|
|
10104
|
-
</div>
|
|
10105
|
-
}
|
|
10106
|
-
|
|
10107
|
-
<!-- Resize Handle (if resizable) -->
|
|
10108
|
-
@if (config().resizable !== false) {
|
|
10109
|
-
<div
|
|
10110
|
-
class="tw-absolute tw-bottom-0 tw-right-0 tw-w-[1.38rem] tw-h-3 tw-cursor-se-resize tw-bg-gray-300 tw-opacity-50 hover:tw-opacity-100"
|
|
10111
|
-
(mousedown)="startResize($event)">
|
|
10112
|
-
</div>
|
|
10113
|
-
}
|
|
10114
|
-
</div>
|
|
10784
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingContainerComponent, isStandalone: true, selector: "cide-ele-floating-container", inputs: { config: "config", isMinimized: "isMinimized", isMaximized: "isMaximized", isVisible: "isVisible", zIndex: "zIndex" }, outputs: { closeEvent: "closeEvent", minimizeEvent: "minimizeEvent", maximizeEvent: "maximizeEvent", clickEvent: "clickEvent" }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: `
|
|
10785
|
+
<div
|
|
10786
|
+
#container
|
|
10787
|
+
class="tw-fixed tw-bg-gray-50 tw-bg-opacity-90 tw-backdrop-blur-sm tw-border tw-border-gray-200 tw-border-opacity-50 tw-rounded-xl tw-shadow-lg tw-transition-all tw-duration-200 tw-flex tw-flex-col"
|
|
10788
|
+
[style.left.px]="isMaximized() ? 20 : position().x"
|
|
10789
|
+
[style.top.px]="isMaximized() ? 20 : position().y"
|
|
10790
|
+
[style.width]="isMaximized() ? 'calc(100vw - 40px)' : (config().width || '400px')"
|
|
10791
|
+
[style.height]="isMaximized() ? 'calc(100vh - 40px)' : (isMinimized() ? 'auto' : (savedHeight || config().height || '500px'))"
|
|
10792
|
+
[style.min-width]="config().minWidth || '300px'"
|
|
10793
|
+
[style.min-height]="isMinimized() ? 'auto' : (config().minHeight || '200px')"
|
|
10794
|
+
[style.max-width]="config().maxWidth || '90vw'"
|
|
10795
|
+
[style.max-height]="config().maxHeight || '90vh'"
|
|
10796
|
+
[style.z-index]="zIndex()"
|
|
10797
|
+
[class.tw-cursor-move]="isDragging()"
|
|
10798
|
+
[class.tw-select-none]="true"
|
|
10799
|
+
(mousedown)="onMouseDown($event)"
|
|
10800
|
+
(mousemove)="onMouseMove($event)"
|
|
10801
|
+
(mouseup)="onMouseUp($event)"
|
|
10802
|
+
(mouseleave)="onMouseUp($event)">
|
|
10803
|
+
|
|
10804
|
+
<!-- Header -->
|
|
10805
|
+
<div
|
|
10806
|
+
class="tw-flex tw-items-center tw-justify-between tw-px-3 tw-py-2 tw-border-b tw-border-gray-200 tw-border-opacity-30 tw-rounded-t-xl tw-cursor-move tw-transition-all tw-duration-200 tw-shadow-sm tw-flex-shrink-0"
|
|
10807
|
+
[style.background]="getHeaderBackground()"
|
|
10808
|
+
(mousedown)="startDrag($event)"
|
|
10809
|
+
(click)="onHeaderClick($event)">
|
|
10810
|
+
|
|
10811
|
+
<!-- Title Section -->
|
|
10812
|
+
<div class="tw-flex tw-items-center tw-space-x-2 tw-text-white">
|
|
10813
|
+
@if (config().icon) {
|
|
10814
|
+
<cide-ele-icon class="tw-w-4 tw-h-4">{{ config().icon }}</cide-ele-icon>
|
|
10815
|
+
}
|
|
10816
|
+
<h2 class="tw-text-sm tw-font-medium tw-truncate">{{ config().title }}</h2>
|
|
10817
|
+
@if (isMinimized()) {
|
|
10818
|
+
<span class="tw-text-xs tw-opacity-75">(Click to restore)</span>
|
|
10819
|
+
}
|
|
10820
|
+
</div>
|
|
10821
|
+
|
|
10822
|
+
<!-- Action Buttons -->
|
|
10823
|
+
<div class="tw-flex tw-items-center tw-space-x-1">
|
|
10824
|
+
@if (config().minimizable !== false) {
|
|
10825
|
+
<button
|
|
10826
|
+
cideEleButton
|
|
10827
|
+
variant="ghost"
|
|
10828
|
+
size="xs"
|
|
10829
|
+
type="button"
|
|
10830
|
+
(click)="minimize(); $event.stopPropagation()"
|
|
10831
|
+
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10832
|
+
[title]="isMinimized() ? 'Restore' : 'Minimize'">
|
|
10833
|
+
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMinimized() ? 'open_in_full' : 'remove' }}</cide-ele-icon>
|
|
10834
|
+
</button>
|
|
10835
|
+
}
|
|
10836
|
+
@if (config().maximizable !== false) {
|
|
10837
|
+
<button
|
|
10838
|
+
cideEleButton
|
|
10839
|
+
variant="ghost"
|
|
10840
|
+
size="xs"
|
|
10841
|
+
type="button"
|
|
10842
|
+
(click)="toggleMaximize(); $event.stopPropagation()"
|
|
10843
|
+
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10844
|
+
[title]="isMaximized() ? 'Restore' : 'Maximize'">
|
|
10845
|
+
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMaximized() ? 'fullscreen_exit' : 'fullscreen' }}</cide-ele-icon>
|
|
10846
|
+
</button>
|
|
10847
|
+
}
|
|
10848
|
+
@if (config().closable !== false) {
|
|
10849
|
+
<button
|
|
10850
|
+
cideEleButton
|
|
10851
|
+
variant="ghost"
|
|
10852
|
+
size="xs"
|
|
10853
|
+
type="button"
|
|
10854
|
+
(click)="close(); $event.stopPropagation()"
|
|
10855
|
+
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10856
|
+
title="Close">
|
|
10857
|
+
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">close</cide-ele-icon>
|
|
10858
|
+
</button>
|
|
10859
|
+
}
|
|
10860
|
+
</div>
|
|
10861
|
+
</div>
|
|
10862
|
+
|
|
10863
|
+
<!-- Content with @defer for performance -->
|
|
10864
|
+
@if (!isMinimized()) {
|
|
10865
|
+
<div
|
|
10866
|
+
class="tw-flex-1 tw-overflow-auto tw-overflow-x-hidden floating-container-content tw-bg-gray-50 tw-bg-opacity-90"
|
|
10867
|
+
[style.height]="isMaximized() ? 'calc(100vh - 100px)' : 'auto'"
|
|
10868
|
+
[style.min-height]="'200px'">
|
|
10869
|
+
@defer (when !isMinimized() && isVisible()) {
|
|
10870
|
+
<div class="tw-p-0 tw-min-h-full">
|
|
10871
|
+
<ng-content></ng-content>
|
|
10872
|
+
</div>
|
|
10873
|
+
} @placeholder {
|
|
10874
|
+
<div class="tw-flex tw-items-center tw-justify-center tw-p-4 tw-min-h-[200px]">
|
|
10875
|
+
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10876
|
+
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading content...</span>
|
|
10877
|
+
</div>
|
|
10878
|
+
}
|
|
10879
|
+
</div>
|
|
10880
|
+
}
|
|
10881
|
+
|
|
10882
|
+
<!-- Resize Handle (if resizable) -->
|
|
10883
|
+
@if (config().resizable !== false) {
|
|
10884
|
+
<div
|
|
10885
|
+
class="tw-absolute tw-bottom-0 tw-right-0 tw-w-[1.38rem] tw-h-3 tw-cursor-se-resize tw-bg-gray-300 tw-opacity-50 hover:tw-opacity-100"
|
|
10886
|
+
(mousedown)="startResize($event)">
|
|
10887
|
+
</div>
|
|
10888
|
+
}
|
|
10889
|
+
</div>
|
|
10115
10890
|
`, isInline: true, styles: [".tw-cursor-move{cursor:move!important}.tw-cursor-se-resize{cursor:se-resize!important}.floating-container-content{overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:#cbd5e1 #f1f5f9;position:relative;flex:1;display:flex;flex-direction:column}.floating-container-content::-webkit-scrollbar{width:8px}.floating-container-content::-webkit-scrollbar-track{background:#f8fafc;border-radius:4px;margin:4px}.floating-container-content::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px;transition:background-color .2s ease;border:1px solid #e2e8f0}.floating-container-content::-webkit-scrollbar-thumb:hover{background:#94a3b8;border-color:#cbd5e1}.floating-container-content::-webkit-scrollbar-thumb:active{background:#64748b}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideSpinnerComponent, selector: "cide-ele-spinner", inputs: ["size", "type"] }] });
|
|
10116
10891
|
}
|
|
10117
10892
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerComponent, decorators: [{
|
|
10118
10893
|
type: Component,
|
|
10119
|
-
args: [{ selector: 'cide-ele-floating-container', standalone: true, imports: [CommonModule, CideEleButtonComponent, CideIconComponent, CideSpinnerComponent], template: `
|
|
10120
|
-
<div
|
|
10121
|
-
#container
|
|
10122
|
-
class="tw-fixed tw-bg-gray-50 tw-bg-opacity-90 tw-backdrop-blur-sm tw-border tw-border-gray-200 tw-border-opacity-50 tw-rounded-xl tw-shadow-lg tw-transition-all tw-duration-200 tw-flex tw-flex-col"
|
|
10123
|
-
[style.left.px]="isMaximized() ? 20 : position().x"
|
|
10124
|
-
[style.top.px]="isMaximized() ? 20 : position().y"
|
|
10125
|
-
[style.width]="isMaximized() ? 'calc(100vw - 40px)' : (config().width || '400px')"
|
|
10126
|
-
[style.height]="isMaximized() ? 'calc(100vh - 40px)' : (isMinimized() ? 'auto' : (savedHeight || config().height || '500px'))"
|
|
10127
|
-
[style.min-width]="config().minWidth || '300px'"
|
|
10128
|
-
[style.min-height]="isMinimized() ? 'auto' : (config().minHeight || '200px')"
|
|
10129
|
-
[style.max-width]="config().maxWidth || '90vw'"
|
|
10130
|
-
[style.max-height]="config().maxHeight || '90vh'"
|
|
10131
|
-
[style.z-index]="zIndex()"
|
|
10132
|
-
[class.tw-cursor-move]="isDragging()"
|
|
10133
|
-
[class.tw-select-none]="true"
|
|
10134
|
-
(mousedown)="onMouseDown($event)"
|
|
10135
|
-
(mousemove)="onMouseMove($event)"
|
|
10136
|
-
(mouseup)="onMouseUp($event)"
|
|
10137
|
-
(mouseleave)="onMouseUp($event)">
|
|
10138
|
-
|
|
10139
|
-
<!-- Header -->
|
|
10140
|
-
<div
|
|
10141
|
-
class="tw-flex tw-items-center tw-justify-between tw-px-3 tw-py-2 tw-border-b tw-border-gray-200 tw-border-opacity-30 tw-rounded-t-xl tw-cursor-move tw-transition-all tw-duration-200 tw-shadow-sm tw-flex-shrink-0"
|
|
10142
|
-
[style.background]="getHeaderBackground()"
|
|
10143
|
-
(mousedown)="startDrag($event)"
|
|
10144
|
-
(click)="onHeaderClick($event)">
|
|
10145
|
-
|
|
10146
|
-
<!-- Title Section -->
|
|
10147
|
-
<div class="tw-flex tw-items-center tw-space-x-2 tw-text-white">
|
|
10148
|
-
@if (config().icon) {
|
|
10149
|
-
<cide-ele-icon class="tw-w-4 tw-h-4">{{ config().icon }}</cide-ele-icon>
|
|
10150
|
-
}
|
|
10151
|
-
<h2 class="tw-text-sm tw-font-medium tw-truncate">{{ config().title }}</h2>
|
|
10152
|
-
@if (isMinimized()) {
|
|
10153
|
-
<span class="tw-text-xs tw-opacity-75">(Click to restore)</span>
|
|
10154
|
-
}
|
|
10155
|
-
</div>
|
|
10156
|
-
|
|
10157
|
-
<!-- Action Buttons -->
|
|
10158
|
-
<div class="tw-flex tw-items-center tw-space-x-1">
|
|
10159
|
-
@if (config().minimizable !== false) {
|
|
10160
|
-
<button
|
|
10161
|
-
cideEleButton
|
|
10162
|
-
variant="ghost"
|
|
10163
|
-
size="xs"
|
|
10164
|
-
type="button"
|
|
10165
|
-
(click)="minimize(); $event.stopPropagation()"
|
|
10166
|
-
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10167
|
-
[title]="isMinimized() ? 'Restore' : 'Minimize'">
|
|
10168
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMinimized() ? 'open_in_full' : 'remove' }}</cide-ele-icon>
|
|
10169
|
-
</button>
|
|
10170
|
-
}
|
|
10171
|
-
@if (config().maximizable !== false) {
|
|
10172
|
-
<button
|
|
10173
|
-
cideEleButton
|
|
10174
|
-
variant="ghost"
|
|
10175
|
-
size="xs"
|
|
10176
|
-
type="button"
|
|
10177
|
-
(click)="toggleMaximize(); $event.stopPropagation()"
|
|
10178
|
-
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10179
|
-
[title]="isMaximized() ? 'Restore' : 'Maximize'">
|
|
10180
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMaximized() ? 'fullscreen_exit' : 'fullscreen' }}</cide-ele-icon>
|
|
10181
|
-
</button>
|
|
10182
|
-
}
|
|
10183
|
-
@if (config().closable !== false) {
|
|
10184
|
-
<button
|
|
10185
|
-
cideEleButton
|
|
10186
|
-
variant="ghost"
|
|
10187
|
-
size="xs"
|
|
10188
|
-
type="button"
|
|
10189
|
-
(click)="close(); $event.stopPropagation()"
|
|
10190
|
-
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10191
|
-
title="Close">
|
|
10192
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">close</cide-ele-icon>
|
|
10193
|
-
</button>
|
|
10194
|
-
}
|
|
10195
|
-
</div>
|
|
10196
|
-
</div>
|
|
10197
|
-
|
|
10198
|
-
<!-- Content with @defer for performance -->
|
|
10199
|
-
@if (!isMinimized()) {
|
|
10200
|
-
<div
|
|
10201
|
-
class="tw-flex-1 tw-overflow-auto tw-overflow-x-hidden floating-container-content tw-bg-gray-50 tw-bg-opacity-90"
|
|
10202
|
-
[style.height]="isMaximized() ? 'calc(100vh - 100px)' : 'auto'"
|
|
10203
|
-
[style.min-height]="'200px'">
|
|
10204
|
-
@defer (when !isMinimized() && isVisible()) {
|
|
10205
|
-
<div class="tw-p-0 tw-min-h-full">
|
|
10206
|
-
<ng-content></ng-content>
|
|
10207
|
-
</div>
|
|
10208
|
-
} @placeholder {
|
|
10209
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4 tw-min-h-[200px]">
|
|
10210
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10211
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading content...</span>
|
|
10212
|
-
</div>
|
|
10213
|
-
}
|
|
10214
|
-
</div>
|
|
10215
|
-
}
|
|
10216
|
-
|
|
10217
|
-
<!-- Resize Handle (if resizable) -->
|
|
10218
|
-
@if (config().resizable !== false) {
|
|
10219
|
-
<div
|
|
10220
|
-
class="tw-absolute tw-bottom-0 tw-right-0 tw-w-[1.38rem] tw-h-3 tw-cursor-se-resize tw-bg-gray-300 tw-opacity-50 hover:tw-opacity-100"
|
|
10221
|
-
(mousedown)="startResize($event)">
|
|
10222
|
-
</div>
|
|
10223
|
-
}
|
|
10224
|
-
</div>
|
|
10894
|
+
args: [{ selector: 'cide-ele-floating-container', standalone: true, imports: [CommonModule, CideEleButtonComponent, CideIconComponent, CideSpinnerComponent], template: `
|
|
10895
|
+
<div
|
|
10896
|
+
#container
|
|
10897
|
+
class="tw-fixed tw-bg-gray-50 tw-bg-opacity-90 tw-backdrop-blur-sm tw-border tw-border-gray-200 tw-border-opacity-50 tw-rounded-xl tw-shadow-lg tw-transition-all tw-duration-200 tw-flex tw-flex-col"
|
|
10898
|
+
[style.left.px]="isMaximized() ? 20 : position().x"
|
|
10899
|
+
[style.top.px]="isMaximized() ? 20 : position().y"
|
|
10900
|
+
[style.width]="isMaximized() ? 'calc(100vw - 40px)' : (config().width || '400px')"
|
|
10901
|
+
[style.height]="isMaximized() ? 'calc(100vh - 40px)' : (isMinimized() ? 'auto' : (savedHeight || config().height || '500px'))"
|
|
10902
|
+
[style.min-width]="config().minWidth || '300px'"
|
|
10903
|
+
[style.min-height]="isMinimized() ? 'auto' : (config().minHeight || '200px')"
|
|
10904
|
+
[style.max-width]="config().maxWidth || '90vw'"
|
|
10905
|
+
[style.max-height]="config().maxHeight || '90vh'"
|
|
10906
|
+
[style.z-index]="zIndex()"
|
|
10907
|
+
[class.tw-cursor-move]="isDragging()"
|
|
10908
|
+
[class.tw-select-none]="true"
|
|
10909
|
+
(mousedown)="onMouseDown($event)"
|
|
10910
|
+
(mousemove)="onMouseMove($event)"
|
|
10911
|
+
(mouseup)="onMouseUp($event)"
|
|
10912
|
+
(mouseleave)="onMouseUp($event)">
|
|
10913
|
+
|
|
10914
|
+
<!-- Header -->
|
|
10915
|
+
<div
|
|
10916
|
+
class="tw-flex tw-items-center tw-justify-between tw-px-3 tw-py-2 tw-border-b tw-border-gray-200 tw-border-opacity-30 tw-rounded-t-xl tw-cursor-move tw-transition-all tw-duration-200 tw-shadow-sm tw-flex-shrink-0"
|
|
10917
|
+
[style.background]="getHeaderBackground()"
|
|
10918
|
+
(mousedown)="startDrag($event)"
|
|
10919
|
+
(click)="onHeaderClick($event)">
|
|
10920
|
+
|
|
10921
|
+
<!-- Title Section -->
|
|
10922
|
+
<div class="tw-flex tw-items-center tw-space-x-2 tw-text-white">
|
|
10923
|
+
@if (config().icon) {
|
|
10924
|
+
<cide-ele-icon class="tw-w-4 tw-h-4">{{ config().icon }}</cide-ele-icon>
|
|
10925
|
+
}
|
|
10926
|
+
<h2 class="tw-text-sm tw-font-medium tw-truncate">{{ config().title }}</h2>
|
|
10927
|
+
@if (isMinimized()) {
|
|
10928
|
+
<span class="tw-text-xs tw-opacity-75">(Click to restore)</span>
|
|
10929
|
+
}
|
|
10930
|
+
</div>
|
|
10931
|
+
|
|
10932
|
+
<!-- Action Buttons -->
|
|
10933
|
+
<div class="tw-flex tw-items-center tw-space-x-1">
|
|
10934
|
+
@if (config().minimizable !== false) {
|
|
10935
|
+
<button
|
|
10936
|
+
cideEleButton
|
|
10937
|
+
variant="ghost"
|
|
10938
|
+
size="xs"
|
|
10939
|
+
type="button"
|
|
10940
|
+
(click)="minimize(); $event.stopPropagation()"
|
|
10941
|
+
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10942
|
+
[title]="isMinimized() ? 'Restore' : 'Minimize'">
|
|
10943
|
+
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMinimized() ? 'open_in_full' : 'remove' }}</cide-ele-icon>
|
|
10944
|
+
</button>
|
|
10945
|
+
}
|
|
10946
|
+
@if (config().maximizable !== false) {
|
|
10947
|
+
<button
|
|
10948
|
+
cideEleButton
|
|
10949
|
+
variant="ghost"
|
|
10950
|
+
size="xs"
|
|
10951
|
+
type="button"
|
|
10952
|
+
(click)="toggleMaximize(); $event.stopPropagation()"
|
|
10953
|
+
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10954
|
+
[title]="isMaximized() ? 'Restore' : 'Maximize'">
|
|
10955
|
+
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMaximized() ? 'fullscreen_exit' : 'fullscreen' }}</cide-ele-icon>
|
|
10956
|
+
</button>
|
|
10957
|
+
}
|
|
10958
|
+
@if (config().closable !== false) {
|
|
10959
|
+
<button
|
|
10960
|
+
cideEleButton
|
|
10961
|
+
variant="ghost"
|
|
10962
|
+
size="xs"
|
|
10963
|
+
type="button"
|
|
10964
|
+
(click)="close(); $event.stopPropagation()"
|
|
10965
|
+
class="tw-w-6 tw-h-6 tw-rounded-lg tw-text-white hover:tw-bg-white hover:tw-bg-opacity-20 tw-transition-all tw-duration-200"
|
|
10966
|
+
title="Close">
|
|
10967
|
+
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">close</cide-ele-icon>
|
|
10968
|
+
</button>
|
|
10969
|
+
}
|
|
10970
|
+
</div>
|
|
10971
|
+
</div>
|
|
10972
|
+
|
|
10973
|
+
<!-- Content with @defer for performance -->
|
|
10974
|
+
@if (!isMinimized()) {
|
|
10975
|
+
<div
|
|
10976
|
+
class="tw-flex-1 tw-overflow-auto tw-overflow-x-hidden floating-container-content tw-bg-gray-50 tw-bg-opacity-90"
|
|
10977
|
+
[style.height]="isMaximized() ? 'calc(100vh - 100px)' : 'auto'"
|
|
10978
|
+
[style.min-height]="'200px'">
|
|
10979
|
+
@defer (when !isMinimized() && isVisible()) {
|
|
10980
|
+
<div class="tw-p-0 tw-min-h-full">
|
|
10981
|
+
<ng-content></ng-content>
|
|
10982
|
+
</div>
|
|
10983
|
+
} @placeholder {
|
|
10984
|
+
<div class="tw-flex tw-items-center tw-justify-center tw-p-4 tw-min-h-[200px]">
|
|
10985
|
+
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10986
|
+
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading content...</span>
|
|
10987
|
+
</div>
|
|
10988
|
+
}
|
|
10989
|
+
</div>
|
|
10990
|
+
}
|
|
10991
|
+
|
|
10992
|
+
<!-- Resize Handle (if resizable) -->
|
|
10993
|
+
@if (config().resizable !== false) {
|
|
10994
|
+
<div
|
|
10995
|
+
class="tw-absolute tw-bottom-0 tw-right-0 tw-w-[1.38rem] tw-h-3 tw-cursor-se-resize tw-bg-gray-300 tw-opacity-50 hover:tw-opacity-100"
|
|
10996
|
+
(mousedown)="startResize($event)">
|
|
10997
|
+
</div>
|
|
10998
|
+
}
|
|
10999
|
+
</div>
|
|
10225
11000
|
`, styles: [".tw-cursor-move{cursor:move!important}.tw-cursor-se-resize{cursor:se-resize!important}.floating-container-content{overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:#cbd5e1 #f1f5f9;position:relative;flex:1;display:flex;flex-direction:column}.floating-container-content::-webkit-scrollbar{width:8px}.floating-container-content::-webkit-scrollbar-track{background:#f8fafc;border-radius:4px;margin:4px}.floating-container-content::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px;transition:background-color .2s ease;border:1px solid #e2e8f0}.floating-container-content::-webkit-scrollbar-thumb:hover{background:#94a3b8;border-color:#cbd5e1}.floating-container-content::-webkit-scrollbar-thumb:active{background:#64748b}\n"] }]
|
|
10226
11001
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
10227
11002
|
type: Input
|
|
@@ -10251,96 +11026,96 @@ class CideEleFloatingContainerManagerComponent {
|
|
|
10251
11026
|
// Computed properties
|
|
10252
11027
|
visibleContainers = computed(() => this.containerService.visibleContainers(), ...(ngDevMode ? [{ debugName: "visibleContainers" }] : []));
|
|
10253
11028
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerManagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10254
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingContainerManagerComponent, isStandalone: true, selector: "cide-ele-floating-container-manager", ngImport: i0, template: `
|
|
10255
|
-
@for (container of visibleContainers(); track container.id) {
|
|
10256
|
-
<cide-ele-floating-container
|
|
10257
|
-
[config]="containerService.getConfigSignal(container.config)"
|
|
10258
|
-
[isMinimized]="containerService.getMinimizedSignal(container.id)"
|
|
10259
|
-
[isMaximized]="containerService.getMaximizedSignal(container.id)"
|
|
10260
|
-
[isVisible]="containerService.getVisibleSignal(container.id)"
|
|
10261
|
-
[zIndex]="containerService.getZIndexSignal(container.id)"
|
|
10262
|
-
(closeEvent)="containerService.onClose($event)"
|
|
10263
|
-
(minimizeEvent)="containerService.onMinimize($event)"
|
|
10264
|
-
(maximizeEvent)="containerService.onMaximize($event)"
|
|
10265
|
-
(clickEvent)="containerService.bringToFront($event)">
|
|
10266
|
-
|
|
10267
|
-
<!-- Dynamic content loading with @defer for performance -->
|
|
10268
|
-
@defer (when container.isVisible) {
|
|
10269
|
-
<div
|
|
10270
|
-
cideEleFloatingDynamic
|
|
10271
|
-
[componentId]="container.config.componentId || container.config.id"
|
|
10272
|
-
[componentConfig]="containerService.getComponentConfig(container.config)"
|
|
10273
|
-
[isVisible]="container.isVisible">
|
|
10274
|
-
</div>
|
|
10275
|
-
} @placeholder {
|
|
10276
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4">
|
|
10277
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10278
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading component...</span>
|
|
10279
|
-
</div>
|
|
10280
|
-
} @error {
|
|
10281
|
-
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-p-4 tw-text-center">
|
|
10282
|
-
<cide-ele-icon class="tw-w-8 tw-h-8 tw-text-red-500 tw-mb-2">error</cide-ele-icon>
|
|
10283
|
-
<p class="tw-text-red-600 tw-font-medium">Failed to load component</p>
|
|
10284
|
-
<p class="tw-text-gray-500 tw-text-sm">Component "{{ container.config.componentId || container.config.id }}" is not available</p>
|
|
10285
|
-
<button
|
|
10286
|
-
cideEleButton
|
|
10287
|
-
variant="outline"
|
|
10288
|
-
size="xs"
|
|
10289
|
-
(click)="containerService.onClose(container.id)"
|
|
10290
|
-
class="tw-mt-2">
|
|
10291
|
-
Close
|
|
10292
|
-
</button>
|
|
10293
|
-
</div>
|
|
10294
|
-
}
|
|
10295
|
-
</cide-ele-floating-container>
|
|
10296
|
-
}
|
|
11029
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingContainerManagerComponent, isStandalone: true, selector: "cide-ele-floating-container-manager", ngImport: i0, template: `
|
|
11030
|
+
@for (container of visibleContainers(); track container.id) {
|
|
11031
|
+
<cide-ele-floating-container
|
|
11032
|
+
[config]="containerService.getConfigSignal(container.config)"
|
|
11033
|
+
[isMinimized]="containerService.getMinimizedSignal(container.id)"
|
|
11034
|
+
[isMaximized]="containerService.getMaximizedSignal(container.id)"
|
|
11035
|
+
[isVisible]="containerService.getVisibleSignal(container.id)"
|
|
11036
|
+
[zIndex]="containerService.getZIndexSignal(container.id)"
|
|
11037
|
+
(closeEvent)="containerService.onClose($event)"
|
|
11038
|
+
(minimizeEvent)="containerService.onMinimize($event)"
|
|
11039
|
+
(maximizeEvent)="containerService.onMaximize($event)"
|
|
11040
|
+
(clickEvent)="containerService.bringToFront($event)">
|
|
11041
|
+
|
|
11042
|
+
<!-- Dynamic content loading with @defer for performance -->
|
|
11043
|
+
@defer (when container.isVisible) {
|
|
11044
|
+
<div
|
|
11045
|
+
cideEleFloatingDynamic
|
|
11046
|
+
[componentId]="container.config.componentId || container.config.id"
|
|
11047
|
+
[componentConfig]="containerService.getComponentConfig(container.config)"
|
|
11048
|
+
[isVisible]="container.isVisible">
|
|
11049
|
+
</div>
|
|
11050
|
+
} @placeholder {
|
|
11051
|
+
<div class="tw-flex tw-items-center tw-justify-center tw-p-4">
|
|
11052
|
+
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
11053
|
+
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading component...</span>
|
|
11054
|
+
</div>
|
|
11055
|
+
} @error {
|
|
11056
|
+
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-p-4 tw-text-center">
|
|
11057
|
+
<cide-ele-icon class="tw-w-8 tw-h-8 tw-text-red-500 tw-mb-2">error</cide-ele-icon>
|
|
11058
|
+
<p class="tw-text-red-600 tw-font-medium">Failed to load component</p>
|
|
11059
|
+
<p class="tw-text-gray-500 tw-text-sm">Component "{{ container.config.componentId || container.config.id }}" is not available</p>
|
|
11060
|
+
<button
|
|
11061
|
+
cideEleButton
|
|
11062
|
+
variant="outline"
|
|
11063
|
+
size="xs"
|
|
11064
|
+
(click)="containerService.onClose(container.id)"
|
|
11065
|
+
class="tw-mt-2">
|
|
11066
|
+
Close
|
|
11067
|
+
</button>
|
|
11068
|
+
</div>
|
|
11069
|
+
}
|
|
11070
|
+
</cide-ele-floating-container>
|
|
11071
|
+
}
|
|
10297
11072
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideEleFloatingContainerComponent, selector: "cide-ele-floating-container", inputs: ["config", "isMinimized", "isMaximized", "isVisible", "zIndex"], outputs: ["closeEvent", "minimizeEvent", "maximizeEvent", "clickEvent"] }, { kind: "component", type: CideSpinnerComponent, selector: "cide-ele-spinner", inputs: ["size", "type"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }], deferBlockDependencies: [() => [Promise.resolve().then(function () { return floatingContainerDynamic_directive; }).then(m => m.CideEleFloatingContainerDynamicDirective)]] });
|
|
10298
11073
|
}
|
|
10299
11074
|
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerManagerComponent, resolveDeferredDeps: () => [Promise.resolve().then(function () { return floatingContainerDynamic_directive; }).then(m => m.CideEleFloatingContainerDynamicDirective)], resolveMetadata: CideEleFloatingContainerDynamicDirective => ({ decorators: [{
|
|
10300
11075
|
type: Component,
|
|
10301
|
-
args: [{ selector: 'cide-ele-floating-container-manager', standalone: true, imports: [CommonModule, CideEleFloatingContainerComponent, CideEleFloatingContainerDynamicDirective, CideSpinnerComponent, CideEleButtonComponent, CideIconComponent], template: `
|
|
10302
|
-
@for (container of visibleContainers(); track container.id) {
|
|
10303
|
-
<cide-ele-floating-container
|
|
10304
|
-
[config]="containerService.getConfigSignal(container.config)"
|
|
10305
|
-
[isMinimized]="containerService.getMinimizedSignal(container.id)"
|
|
10306
|
-
[isMaximized]="containerService.getMaximizedSignal(container.id)"
|
|
10307
|
-
[isVisible]="containerService.getVisibleSignal(container.id)"
|
|
10308
|
-
[zIndex]="containerService.getZIndexSignal(container.id)"
|
|
10309
|
-
(closeEvent)="containerService.onClose($event)"
|
|
10310
|
-
(minimizeEvent)="containerService.onMinimize($event)"
|
|
10311
|
-
(maximizeEvent)="containerService.onMaximize($event)"
|
|
10312
|
-
(clickEvent)="containerService.bringToFront($event)">
|
|
10313
|
-
|
|
10314
|
-
<!-- Dynamic content loading with @defer for performance -->
|
|
10315
|
-
@defer (when container.isVisible) {
|
|
10316
|
-
<div
|
|
10317
|
-
cideEleFloatingDynamic
|
|
10318
|
-
[componentId]="container.config.componentId || container.config.id"
|
|
10319
|
-
[componentConfig]="containerService.getComponentConfig(container.config)"
|
|
10320
|
-
[isVisible]="container.isVisible">
|
|
10321
|
-
</div>
|
|
10322
|
-
} @placeholder {
|
|
10323
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4">
|
|
10324
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10325
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading component...</span>
|
|
10326
|
-
</div>
|
|
10327
|
-
} @error {
|
|
10328
|
-
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-p-4 tw-text-center">
|
|
10329
|
-
<cide-ele-icon class="tw-w-8 tw-h-8 tw-text-red-500 tw-mb-2">error</cide-ele-icon>
|
|
10330
|
-
<p class="tw-text-red-600 tw-font-medium">Failed to load component</p>
|
|
10331
|
-
<p class="tw-text-gray-500 tw-text-sm">Component "{{ container.config.componentId || container.config.id }}" is not available</p>
|
|
10332
|
-
<button
|
|
10333
|
-
cideEleButton
|
|
10334
|
-
variant="outline"
|
|
10335
|
-
size="xs"
|
|
10336
|
-
(click)="containerService.onClose(container.id)"
|
|
10337
|
-
class="tw-mt-2">
|
|
10338
|
-
Close
|
|
10339
|
-
</button>
|
|
10340
|
-
</div>
|
|
10341
|
-
}
|
|
10342
|
-
</cide-ele-floating-container>
|
|
10343
|
-
}
|
|
11076
|
+
args: [{ selector: 'cide-ele-floating-container-manager', standalone: true, imports: [CommonModule, CideEleFloatingContainerComponent, CideEleFloatingContainerDynamicDirective, CideSpinnerComponent, CideEleButtonComponent, CideIconComponent], template: `
|
|
11077
|
+
@for (container of visibleContainers(); track container.id) {
|
|
11078
|
+
<cide-ele-floating-container
|
|
11079
|
+
[config]="containerService.getConfigSignal(container.config)"
|
|
11080
|
+
[isMinimized]="containerService.getMinimizedSignal(container.id)"
|
|
11081
|
+
[isMaximized]="containerService.getMaximizedSignal(container.id)"
|
|
11082
|
+
[isVisible]="containerService.getVisibleSignal(container.id)"
|
|
11083
|
+
[zIndex]="containerService.getZIndexSignal(container.id)"
|
|
11084
|
+
(closeEvent)="containerService.onClose($event)"
|
|
11085
|
+
(minimizeEvent)="containerService.onMinimize($event)"
|
|
11086
|
+
(maximizeEvent)="containerService.onMaximize($event)"
|
|
11087
|
+
(clickEvent)="containerService.bringToFront($event)">
|
|
11088
|
+
|
|
11089
|
+
<!-- Dynamic content loading with @defer for performance -->
|
|
11090
|
+
@defer (when container.isVisible) {
|
|
11091
|
+
<div
|
|
11092
|
+
cideEleFloatingDynamic
|
|
11093
|
+
[componentId]="container.config.componentId || container.config.id"
|
|
11094
|
+
[componentConfig]="containerService.getComponentConfig(container.config)"
|
|
11095
|
+
[isVisible]="container.isVisible">
|
|
11096
|
+
</div>
|
|
11097
|
+
} @placeholder {
|
|
11098
|
+
<div class="tw-flex tw-items-center tw-justify-center tw-p-4">
|
|
11099
|
+
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
11100
|
+
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading component...</span>
|
|
11101
|
+
</div>
|
|
11102
|
+
} @error {
|
|
11103
|
+
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-p-4 tw-text-center">
|
|
11104
|
+
<cide-ele-icon class="tw-w-8 tw-h-8 tw-text-red-500 tw-mb-2">error</cide-ele-icon>
|
|
11105
|
+
<p class="tw-text-red-600 tw-font-medium">Failed to load component</p>
|
|
11106
|
+
<p class="tw-text-gray-500 tw-text-sm">Component "{{ container.config.componentId || container.config.id }}" is not available</p>
|
|
11107
|
+
<button
|
|
11108
|
+
cideEleButton
|
|
11109
|
+
variant="outline"
|
|
11110
|
+
size="xs"
|
|
11111
|
+
(click)="containerService.onClose(container.id)"
|
|
11112
|
+
class="tw-mt-2">
|
|
11113
|
+
Close
|
|
11114
|
+
</button>
|
|
11115
|
+
</div>
|
|
11116
|
+
}
|
|
11117
|
+
</cide-ele-floating-container>
|
|
11118
|
+
}
|
|
10344
11119
|
` }]
|
|
10345
11120
|
}], ctorParameters: null, propDecorators: null }) });
|
|
10346
11121
|
|