cloud-ide-element 1.1.12 → 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 +705 -669
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +4 -3
- 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) {
|
|
@@ -582,6 +583,8 @@ class CideInputComponent {
|
|
|
582
583
|
showDateTimePicker = false;
|
|
583
584
|
dateTimeStep = 'date';
|
|
584
585
|
dateTimePickerPortalId = `datetime-picker-${this.randomString()}`;
|
|
586
|
+
// ViewChild for datetime picker template
|
|
587
|
+
dateTimePickerTemplate;
|
|
585
588
|
// ==================================METHODS FOR CUSTOM FORM COMPONENT=============================
|
|
586
589
|
// FOR ANGULAR CALLED BY UI
|
|
587
590
|
/**
|
|
@@ -822,11 +825,26 @@ class CideInputComponent {
|
|
|
822
825
|
return false;
|
|
823
826
|
}
|
|
824
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
|
+
}
|
|
825
843
|
else if (type == 'datetime-local') {
|
|
826
844
|
if (typeof (value) == 'string') {
|
|
827
845
|
if (value?.length > 0) {
|
|
828
|
-
// Validate datetime-local format (YYYY-MM-DDTHH:mm)
|
|
829
|
-
const datetimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}
|
|
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})?$/;
|
|
830
848
|
return datetimeRegex.test(value) && !isNaN(Date.parse(value));
|
|
831
849
|
}
|
|
832
850
|
else {
|
|
@@ -1321,6 +1339,10 @@ class CideInputComponent {
|
|
|
1321
1339
|
this.ngModel = formattedDate;
|
|
1322
1340
|
this.ngModelChange?.emit(formattedDate);
|
|
1323
1341
|
this.onChange(formattedDate);
|
|
1342
|
+
// Mark as touched and validate
|
|
1343
|
+
this.isTouched = true;
|
|
1344
|
+
this.onTouched();
|
|
1345
|
+
this.isControlValid(formattedDate);
|
|
1324
1346
|
// Regenerate calendar to update selection
|
|
1325
1347
|
this.generateCalendar();
|
|
1326
1348
|
// Close date picker
|
|
@@ -1441,6 +1463,10 @@ class CideInputComponent {
|
|
|
1441
1463
|
this.ngModel = timeString;
|
|
1442
1464
|
this.ngModelChange.emit(timeString);
|
|
1443
1465
|
this.onChange(timeString);
|
|
1466
|
+
// Mark as touched and validate
|
|
1467
|
+
this.isTouched = true;
|
|
1468
|
+
this.onTouched();
|
|
1469
|
+
this.isControlValid(timeString);
|
|
1444
1470
|
this.closeTimePicker();
|
|
1445
1471
|
}
|
|
1446
1472
|
/**
|
|
@@ -1505,9 +1531,9 @@ class CideInputComponent {
|
|
|
1505
1531
|
const inputElement = document.getElementById(this.id || this.idRandom);
|
|
1506
1532
|
if (!inputElement)
|
|
1507
1533
|
return;
|
|
1508
|
-
// Use
|
|
1534
|
+
// Use combined datetime picker template (shows date and time together)
|
|
1509
1535
|
const portalConfig = {
|
|
1510
|
-
template: this.
|
|
1536
|
+
template: this.dateTimePickerTemplate,
|
|
1511
1537
|
context: { $implicit: this },
|
|
1512
1538
|
triggerElement: inputElement,
|
|
1513
1539
|
viewContainerRef: this.viewContainerRef,
|
|
@@ -1534,34 +1560,15 @@ class CideInputComponent {
|
|
|
1534
1560
|
this.portalService.destroyPortal(this.dateTimePickerPortalId);
|
|
1535
1561
|
}
|
|
1536
1562
|
/**
|
|
1537
|
-
* @description Select date in datetime picker (
|
|
1563
|
+
* @description Select date in datetime picker (just updates the selected date, time stays visible)
|
|
1538
1564
|
*/
|
|
1539
1565
|
selectDateTimeDate(dayInfo) {
|
|
1540
1566
|
if (!dayInfo.isCurrentMonth)
|
|
1541
1567
|
return;
|
|
1568
|
+
// Just update the selected date - time picker remains visible on the right
|
|
1542
1569
|
this.selectedDate = dayInfo.date;
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
this.portalService.destroyPortal(this.dateTimePickerPortalId);
|
|
1546
|
-
const inputElement = document.getElementById(this.id || this.idRandom);
|
|
1547
|
-
if (!inputElement)
|
|
1548
|
-
return;
|
|
1549
|
-
const portalConfig = {
|
|
1550
|
-
template: this.timePickerTemplate,
|
|
1551
|
-
context: { $implicit: this },
|
|
1552
|
-
triggerElement: inputElement,
|
|
1553
|
-
viewContainerRef: this.viewContainerRef,
|
|
1554
|
-
className: 'cide-datetime-picker-portal',
|
|
1555
|
-
position: 'bottom',
|
|
1556
|
-
align: 'left',
|
|
1557
|
-
offset: { x: 0, y: 8 },
|
|
1558
|
-
closeOnOutsideClick: true,
|
|
1559
|
-
closeOnEscape: true
|
|
1560
|
-
};
|
|
1561
|
-
this.portalService.createTemplatePortal(this.dateTimePickerPortalId, portalConfig);
|
|
1562
|
-
this.portalService.registerCloseCallback(this.dateTimePickerPortalId, () => {
|
|
1563
|
-
this.closeDateTimePicker();
|
|
1564
|
-
});
|
|
1570
|
+
// Regenerate calendar to update selection
|
|
1571
|
+
this.generateCalendar();
|
|
1565
1572
|
}
|
|
1566
1573
|
/**
|
|
1567
1574
|
* @description Apply datetime (combines date and time)
|
|
@@ -1585,6 +1592,10 @@ class CideInputComponent {
|
|
|
1585
1592
|
this.ngModel = dateTimeString;
|
|
1586
1593
|
this.ngModelChange.emit(dateTimeString);
|
|
1587
1594
|
this.onChange(dateTimeString);
|
|
1595
|
+
// Mark as touched and validate
|
|
1596
|
+
this.isTouched = true;
|
|
1597
|
+
this.onTouched();
|
|
1598
|
+
this.isControlValid(dateTimeString);
|
|
1588
1599
|
this.closeDateTimePicker();
|
|
1589
1600
|
}
|
|
1590
1601
|
/**
|
|
@@ -1630,7 +1641,7 @@ class CideInputComponent {
|
|
|
1630
1641
|
useExisting: forwardRef(() => CideInputComponent),
|
|
1631
1642
|
},
|
|
1632
1643
|
CapitalizePipe
|
|
1633
|
-
], viewQueries: [{ propertyName: "datePickerTemplate", first: true, predicate: ["datePickerTemplate"], descendants: true, static: true }, { propertyName: "timePickerTemplate", first: true, predicate: ["timePickerTemplate"], 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 === 'datetime-local' || 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 <!-- Time Input Wrapper -->\n @if (isTimeType()) {\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\n <!-- Time 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]=\"getTimeDisplayValue()\" 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 time -->\n @if (!ngModel && placeholder) {\n <div class=\"cide-input-date-overlay\">\n {{placeholder}}\n </div>\n }\n\n <!-- Time picker is now rendered as a portal appended to body -->\n </div>\n }\n\n <!-- DateTime Local Input -->\n @if (type === 'datetime-local') {\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\n <!-- DateTime 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]=\"getDateTimeDisplayValue()\" 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 datetime -->\n @if (!ngModel && placeholder) {\n <div class=\"cide-input-date-overlay\">\n {{placeholder}}\n </div>\n }\n\n <!-- DateTime picker is now rendered as a portal appended to body -->\n </div>\n }\n\n <!-- Regular Input (non-date, non-time, non-datetime-local, non-url) -->\n @if (type !== 'date' && !isTimeType() && type !== 'datetime-local' && 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)=\"showDateTimePicker ? selectDateTimeDate(dayInfo) : 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>\n\n<!-- Time Picker Template -->\n<ng-template #timePickerTemplate>\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-2xl tw-shadow-lg tw-p-6 tw-w-80\">\n <!-- Time Picker Header -->\n <div class=\"tw-text-center tw-mb-6\">\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-800 tw-m-0\">Time</h3>\n </div>\n\n <!-- Time Spinners Container -->\n <div class=\"tw-flex tw-items-center tw-justify-center tw-gap-4 tw-mb-6\">\n <!-- Hours -->\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('hours', 'up')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_less</span>\n </button>\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-my-2 tw-w-16 tw-text-center\">\n {{ timeHours }}\n </div>\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('hours', 'down')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_more</span>\n </button>\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">hour</span>\n </div>\n\n <!-- Colon Separator -->\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-mb-6\">:</div>\n\n <!-- Minutes -->\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('minutes', 'up')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_less</span>\n </button>\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-my-2 tw-w-16 tw-text-center\">\n {{ timeMinutes.toString().padStart(2, '0') }}\n </div>\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('minutes', 'down')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_more</span>\n </button>\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">min</span>\n </div>\n\n <!-- Colon Separator -->\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-mb-6\">:</div>\n\n <!-- Seconds -->\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('seconds', 'up')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_less</span>\n </button>\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-my-2 tw-w-16 tw-text-center\">\n {{ timeSeconds.toString().padStart(2, '0') }}\n </div>\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('seconds', 'down')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_more</span>\n </button>\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">sec</span>\n </div>\n </div>\n\n <!-- AM/PM Toggle -->\n <div class=\"tw-flex tw-justify-center tw-mb-6\">\n <div class=\"tw-inline-flex tw-rounded-lg tw-bg-gray-100 tw-p-1\">\n <button \n type=\"button\"\n class=\"tw-px-6 tw-py-2 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\n [class.tw-bg-white]=\"timeFormat === 'AM'\"\n [class.tw-text-gray-800]=\"timeFormat === 'AM'\"\n [class.tw-shadow]=\"timeFormat === 'AM'\"\n [class.tw-text-gray-600]=\"timeFormat !== 'AM'\"\n (click)=\"timeFormat = 'AM'\">\n AM\n </button>\n <button \n type=\"button\"\n class=\"tw-px-6 tw-py-2 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\n [class.tw-bg-white]=\"timeFormat === 'PM'\"\n [class.tw-text-gray-800]=\"timeFormat === 'PM'\"\n [class.tw-shadow]=\"timeFormat === 'PM'\"\n [class.tw-text-gray-600]=\"timeFormat !== 'PM'\"\n (click)=\"timeFormat = 'PM'\">\n PM\n </button>\n </div>\n </div>\n\n <!-- Current Time Display -->\n <div class=\"tw-text-center tw-mb-6\">\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ timeHours }}:{{ timeMinutes.toString().padStart(2, '0') }}:{{ timeSeconds.toString().padStart(2, '0') }} {{ timeFormat }}\n </span>\n </div>\n\n <!-- Time Picker Footer -->\n <div class=\"tw-flex tw-gap-3\">\n <button \n type=\"button\" \n class=\"tw-flex-1 tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-gray-700 tw-bg-white tw-border tw-border-gray-300 hover:tw-bg-gray-50 tw-rounded-lg tw-transition-colors\"\n (click)=\"showDateTimePicker ? closeDateTimePicker() : closeTimePicker()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"tw-flex-1 tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700 tw-rounded-lg tw-transition-colors tw-border-0\"\n (click)=\"showDateTimePicker ? applyDateTime() : applyTime()\">\n OK\n </button>\n </div>\n\n <!-- Back to Date Button (only for datetime picker) -->\n @if (showDateTimePicker && dateTimeStep === 'time') {\n <div class=\"tw-text-center tw-mt-4\">\n <button \n type=\"button\" \n class=\"tw-text-sm tw-text-blue-600 hover:tw-text-blue-700 tw-underline tw-border-0 tw-bg-transparent tw-p-0\"\n (click)=\"backToDateSelection()\">\n \u2190 Back to Date\n </button>\n </div>\n }\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}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:
|
|
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:
|
|
1634
1645
|
// directives
|
|
1635
1646
|
CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type:
|
|
1636
1647
|
// for ngModel
|
|
@@ -1656,7 +1667,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
1656
1667
|
useExisting: forwardRef(() => CideInputComponent),
|
|
1657
1668
|
},
|
|
1658
1669
|
CapitalizePipe
|
|
1659
|
-
], 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 === 'datetime-local' || 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 <!-- Time Input Wrapper -->\n @if (isTimeType()) {\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\n <!-- Time 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]=\"getTimeDisplayValue()\" 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 time -->\n @if (!ngModel && placeholder) {\n <div class=\"cide-input-date-overlay\">\n {{placeholder}}\n </div>\n }\n\n <!-- Time picker is now rendered as a portal appended to body -->\n </div>\n }\n\n <!-- DateTime Local Input -->\n @if (type === 'datetime-local') {\n <div class=\"cide-input-date-wrapper\" [ngClass]=\"{'cide-input-date-has-value': ngModel}\">\n <!-- DateTime 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]=\"getDateTimeDisplayValue()\" 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 datetime -->\n @if (!ngModel && placeholder) {\n <div class=\"cide-input-date-overlay\">\n {{placeholder}}\n </div>\n }\n\n <!-- DateTime picker is now rendered as a portal appended to body -->\n </div>\n }\n\n <!-- Regular Input (non-date, non-time, non-datetime-local, non-url) -->\n @if (type !== 'date' && !isTimeType() && type !== 'datetime-local' && 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)=\"showDateTimePicker ? selectDateTimeDate(dayInfo) : 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>\n\n<!-- Time Picker Template -->\n<ng-template #timePickerTemplate>\n <div class=\"tw-bg-white tw-border tw-border-gray-300 tw-rounded-2xl tw-shadow-lg tw-p-6 tw-w-80\">\n <!-- Time Picker Header -->\n <div class=\"tw-text-center tw-mb-6\">\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-800 tw-m-0\">Time</h3>\n </div>\n\n <!-- Time Spinners Container -->\n <div class=\"tw-flex tw-items-center tw-justify-center tw-gap-4 tw-mb-6\">\n <!-- Hours -->\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('hours', 'up')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_less</span>\n </button>\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-my-2 tw-w-16 tw-text-center\">\n {{ timeHours }}\n </div>\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('hours', 'down')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_more</span>\n </button>\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">hour</span>\n </div>\n\n <!-- Colon Separator -->\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-mb-6\">:</div>\n\n <!-- Minutes -->\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('minutes', 'up')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_less</span>\n </button>\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-my-2 tw-w-16 tw-text-center\">\n {{ timeMinutes.toString().padStart(2, '0') }}\n </div>\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('minutes', 'down')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_more</span>\n </button>\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">min</span>\n </div>\n\n <!-- Colon Separator -->\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-mb-6\">:</div>\n\n <!-- Seconds -->\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('seconds', 'up')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_less</span>\n </button>\n <div class=\"tw-text-4xl tw-font-normal tw-text-gray-800 tw-my-2 tw-w-16 tw-text-center\">\n {{ timeSeconds.toString().padStart(2, '0') }}\n </div>\n <button \n type=\"button\"\n class=\"tw-w-10 tw-h-10 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\"\n (click)=\"adjustTime('seconds', 'down')\">\n <span class=\"material-symbols-outlined tw-text-xl\">expand_more</span>\n </button>\n <span class=\"tw-text-xs tw-text-gray-500 tw-mt-2\">sec</span>\n </div>\n </div>\n\n <!-- AM/PM Toggle -->\n <div class=\"tw-flex tw-justify-center tw-mb-6\">\n <div class=\"tw-inline-flex tw-rounded-lg tw-bg-gray-100 tw-p-1\">\n <button \n type=\"button\"\n class=\"tw-px-6 tw-py-2 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\n [class.tw-bg-white]=\"timeFormat === 'AM'\"\n [class.tw-text-gray-800]=\"timeFormat === 'AM'\"\n [class.tw-shadow]=\"timeFormat === 'AM'\"\n [class.tw-text-gray-600]=\"timeFormat !== 'AM'\"\n (click)=\"timeFormat = 'AM'\">\n AM\n </button>\n <button \n type=\"button\"\n class=\"tw-px-6 tw-py-2 tw-text-sm tw-font-medium tw-rounded-md tw-transition-colors tw-border-0\"\n [class.tw-bg-white]=\"timeFormat === 'PM'\"\n [class.tw-text-gray-800]=\"timeFormat === 'PM'\"\n [class.tw-shadow]=\"timeFormat === 'PM'\"\n [class.tw-text-gray-600]=\"timeFormat !== 'PM'\"\n (click)=\"timeFormat = 'PM'\">\n PM\n </button>\n </div>\n </div>\n\n <!-- Current Time Display -->\n <div class=\"tw-text-center tw-mb-6\">\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ timeHours }}:{{ timeMinutes.toString().padStart(2, '0') }}:{{ timeSeconds.toString().padStart(2, '0') }} {{ timeFormat }}\n </span>\n </div>\n\n <!-- Time Picker Footer -->\n <div class=\"tw-flex tw-gap-3\">\n <button \n type=\"button\" \n class=\"tw-flex-1 tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-gray-700 tw-bg-white tw-border tw-border-gray-300 hover:tw-bg-gray-50 tw-rounded-lg tw-transition-colors\"\n (click)=\"showDateTimePicker ? closeDateTimePicker() : closeTimePicker()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"tw-flex-1 tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700 tw-rounded-lg tw-transition-colors tw-border-0\"\n (click)=\"showDateTimePicker ? applyDateTime() : applyTime()\">\n OK\n </button>\n </div>\n\n <!-- Back to Date Button (only for datetime picker) -->\n @if (showDateTimePicker && dateTimeStep === 'time') {\n <div class=\"tw-text-center tw-mt-4\">\n <button \n type=\"button\" \n class=\"tw-text-sm tw-text-blue-600 hover:tw-text-blue-700 tw-underline tw-border-0 tw-bg-transparent tw-p-0\"\n (click)=\"backToDateSelection()\">\n \u2190 Back to Date\n </button>\n </div>\n }\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}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"] }]
|
|
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"] }]
|
|
1660
1671
|
}], ctorParameters: () => [], propDecorators: { fill: [{
|
|
1661
1672
|
type: Input
|
|
1662
1673
|
}], label: [{
|
|
@@ -1719,6 +1730,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
1719
1730
|
}], timePickerTemplate: [{
|
|
1720
1731
|
type: ViewChild,
|
|
1721
1732
|
args: ['timePickerTemplate', { static: true }]
|
|
1733
|
+
}], dateTimePickerTemplate: [{
|
|
1734
|
+
type: ViewChild,
|
|
1735
|
+
args: ['dateTimePickerTemplate', { static: true }]
|
|
1722
1736
|
}] } });
|
|
1723
1737
|
|
|
1724
1738
|
class CideSelectOptionComponent {
|
|
@@ -2560,7 +2574,7 @@ class CideSelectComponent {
|
|
|
2560
2574
|
multi: true,
|
|
2561
2575
|
useExisting: forwardRef(() => CideSelectComponent),
|
|
2562
2576
|
}
|
|
2563
|
-
], 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 }] });
|
|
2564
2578
|
}
|
|
2565
2579
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSelectComponent, decorators: [{
|
|
2566
2580
|
type: Component,
|
|
@@ -2575,7 +2589,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
2575
2589
|
multi: true,
|
|
2576
2590
|
useExisting: forwardRef(() => CideSelectComponent),
|
|
2577
2591
|
}
|
|
2578
|
-
], 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"] }]
|
|
2579
2593
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { optionComponents: [{
|
|
2580
2594
|
type: ContentChildren,
|
|
2581
2595
|
args: [CideSelectOptionComponent]
|
|
@@ -5102,7 +5116,7 @@ class CideEleFileInputComponent {
|
|
|
5102
5116
|
useExisting: CideEleFileInputComponent,
|
|
5103
5117
|
multi: true
|
|
5104
5118
|
}
|
|
5105
|
-
], 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"] }] });
|
|
5106
5120
|
}
|
|
5107
5121
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
5108
5122
|
type: Component,
|
|
@@ -5117,7 +5131,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
5117
5131
|
useExisting: CideEleFileInputComponent,
|
|
5118
5132
|
multi: true
|
|
5119
5133
|
}
|
|
5120
|
-
], 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> " }]
|
|
5121
5135
|
}], ctorParameters: () => [], propDecorators: { label: [{
|
|
5122
5136
|
type: Input
|
|
5123
5137
|
}], accept: [{
|
|
@@ -5597,14 +5611,14 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
5597
5611
|
});
|
|
5598
5612
|
}
|
|
5599
5613
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5600
|
-
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"] }] });
|
|
5601
5615
|
}
|
|
5602
5616
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, decorators: [{
|
|
5603
5617
|
type: Component,
|
|
5604
5618
|
args: [{ selector: 'cide-ele-floating-file-uploader', standalone: true, imports: [
|
|
5605
5619
|
CommonModule,
|
|
5606
5620
|
CideIconComponent
|
|
5607
|
-
], 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}" }]
|
|
5608
5622
|
}], ctorParameters: () => [], propDecorators: { fileInputRef: [{
|
|
5609
5623
|
type: ViewChild,
|
|
5610
5624
|
args: ['fileInput']
|
|
@@ -5708,7 +5722,7 @@ class CideTextareaComponent {
|
|
|
5708
5722
|
multi: true,
|
|
5709
5723
|
useExisting: forwardRef(() => CideTextareaComponent),
|
|
5710
5724
|
}
|
|
5711
|
-
], 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"] }] });
|
|
5712
5726
|
}
|
|
5713
5727
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideTextareaComponent, decorators: [{
|
|
5714
5728
|
type: Component,
|
|
@@ -5723,7 +5737,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
5723
5737
|
multi: true,
|
|
5724
5738
|
useExisting: forwardRef(() => CideTextareaComponent),
|
|
5725
5739
|
}
|
|
5726
|
-
], 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>" }]
|
|
5727
5741
|
}], propDecorators: { label: [{
|
|
5728
5742
|
type: Input
|
|
5729
5743
|
}], labelHide: [{
|
|
@@ -6008,11 +6022,11 @@ class CideEleBreadcrumbComponent {
|
|
|
6008
6022
|
this.localLoading.set(value);
|
|
6009
6023
|
}
|
|
6010
6024
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6011
|
-
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 });
|
|
6012
6026
|
}
|
|
6013
6027
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleBreadcrumbComponent, decorators: [{
|
|
6014
6028
|
type: Component,
|
|
6015
|
-
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"] }]
|
|
6016
6030
|
}], ctorParameters: () => [], propDecorators: { itemClick: [{
|
|
6017
6031
|
type: Output
|
|
6018
6032
|
}], dropdownOptionClick: [{
|
|
@@ -7730,24 +7744,24 @@ class CideFormFieldErrorComponent {
|
|
|
7730
7744
|
.join(' ');
|
|
7731
7745
|
}
|
|
7732
7746
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideFormFieldErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7733
|
-
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: `
|
|
7734
|
-
@if (isFormGroupMode()) {
|
|
7735
|
-
<!-- Form Group Error Display -->
|
|
7736
|
-
@if (formErrors().length > 0) {
|
|
7737
|
-
<div class="tw-flex tw-items-center tw-gap-2 tw-text-red-600 tw-text-sm">
|
|
7738
|
-
<cide-ele-icon class="tw-text-red-500">error</cide-ele-icon>
|
|
7739
|
-
<span>{{ getFormGroupErrorMessage() }}</span>
|
|
7740
|
-
</div>
|
|
7741
|
-
}
|
|
7742
|
-
} @else {
|
|
7743
|
-
<!-- Individual Field Error Display -->
|
|
7744
|
-
@if (control && control.invalid && (control.dirty || control.touched)) {
|
|
7745
|
-
<div class="tw-text-red-500 tw-text-xs tw-mt-1 tw-flex tw-items-center tw-gap-1">
|
|
7746
|
-
<cide-ele-icon class="tw-text-xs">error</cide-ele-icon>
|
|
7747
|
-
<span>{{ getErrorMessage() }}</span>
|
|
7748
|
-
</div>
|
|
7749
|
-
}
|
|
7750
|
-
}
|
|
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
|
+
}
|
|
7751
7765
|
`, isInline: true, dependencies: [{ kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
7752
7766
|
}
|
|
7753
7767
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideFormFieldErrorComponent, decorators: [{
|
|
@@ -7755,24 +7769,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
7755
7769
|
args: [{
|
|
7756
7770
|
selector: 'cide-form-field-error',
|
|
7757
7771
|
standalone: true,
|
|
7758
|
-
template: `
|
|
7759
|
-
@if (isFormGroupMode()) {
|
|
7760
|
-
<!-- Form Group Error Display -->
|
|
7761
|
-
@if (formErrors().length > 0) {
|
|
7762
|
-
<div class="tw-flex tw-items-center tw-gap-2 tw-text-red-600 tw-text-sm">
|
|
7763
|
-
<cide-ele-icon class="tw-text-red-500">error</cide-ele-icon>
|
|
7764
|
-
<span>{{ getFormGroupErrorMessage() }}</span>
|
|
7765
|
-
</div>
|
|
7766
|
-
}
|
|
7767
|
-
} @else {
|
|
7768
|
-
<!-- Individual Field Error Display -->
|
|
7769
|
-
@if (control && control.invalid && (control.dirty || control.touched)) {
|
|
7770
|
-
<div class="tw-text-red-500 tw-text-xs tw-mt-1 tw-flex tw-items-center tw-gap-1">
|
|
7771
|
-
<cide-ele-icon class="tw-text-xs">error</cide-ele-icon>
|
|
7772
|
-
<span>{{ getErrorMessage() }}</span>
|
|
7773
|
-
</div>
|
|
7774
|
-
}
|
|
7775
|
-
}
|
|
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
|
+
}
|
|
7776
7790
|
`,
|
|
7777
7791
|
imports: [CideIconComponent]
|
|
7778
7792
|
}]
|
|
@@ -7992,146 +8006,168 @@ class CideEleToastNotificationComponent {
|
|
|
7992
8006
|
return notification.id;
|
|
7993
8007
|
}
|
|
7994
8008
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleToastNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7995
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleToastNotificationComponent, isStandalone: true, selector: "cide-ele-toast-notification", ngImport: i0, template: `
|
|
7996
|
-
<!-- Toast Container -->
|
|
7997
|
-
<div class="toast-container">
|
|
7998
|
-
<div
|
|
7999
|
-
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
8000
|
-
class="toast-notification"
|
|
8001
|
-
[class]="getToastClass(notification)"
|
|
8002
|
-
[@toastAnimation]>
|
|
8003
|
-
|
|
8004
|
-
<!-- Toast Content -->
|
|
8005
|
-
<div class="toast-content">
|
|
8006
|
-
<!-- Icon -->
|
|
8007
|
-
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
8008
|
-
<cide-ele-icon size="sm">
|
|
8009
|
-
{{ notification.icon }}
|
|
8010
|
-
</cide-ele-icon>
|
|
8011
|
-
</div>
|
|
8012
|
-
|
|
8013
|
-
<!-- Text Content -->
|
|
8014
|
-
<div class="toast-text">
|
|
8015
|
-
<div *ngIf="notification.title" class="toast-title">
|
|
8016
|
-
{{ notification.title }}
|
|
8017
|
-
</div>
|
|
8018
|
-
<div class="toast-message">
|
|
8019
|
-
{{ notification.message }}
|
|
8020
|
-
</div>
|
|
8021
|
-
</div>
|
|
8022
|
-
|
|
8023
|
-
<!-- Close Button -->
|
|
8024
|
-
<button
|
|
8025
|
-
type="button"
|
|
8026
|
-
class="toast-close"
|
|
8027
|
-
(click)="removeNotification(notification.id)"
|
|
8028
|
-
aria-label="Close notification">
|
|
8029
|
-
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
8030
|
-
</button>
|
|
8031
|
-
</div>
|
|
8032
|
-
|
|
8033
|
-
<!-- Undo Button -->
|
|
8034
|
-
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
8035
|
-
<button
|
|
8036
|
-
type="button"
|
|
8037
|
-
class="toast-undo-btn"
|
|
8038
|
-
(click)="executeUndo(notification.id)">
|
|
8039
|
-
{{ notification.undoText }}
|
|
8040
|
-
</button>
|
|
8041
|
-
</div>
|
|
8042
|
-
|
|
8043
|
-
<!-- Progress Bar -->
|
|
8044
|
-
<div
|
|
8045
|
-
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
8046
|
-
class="toast-progress"
|
|
8047
|
-
[style.animation-duration]="notification.duration + 'ms'">
|
|
8048
|
-
</div>
|
|
8049
|
-
|
|
8050
|
-
<!-- Custom Progress Bar -->
|
|
8051
|
-
<div
|
|
8052
|
-
*ngIf="notification.showProgress"
|
|
8053
|
-
class="toast-custom-progress">
|
|
8054
|
-
<div
|
|
8055
|
-
class="toast-custom-progress-bar"
|
|
8056
|
-
[style.width.%]="notification.progress || 0">
|
|
8057
|
-
</div>
|
|
8058
|
-
</div>
|
|
8059
|
-
</div>
|
|
8060
|
-
</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>
|
|
8061
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: [
|
|
8062
|
-
|
|
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
|
+
])
|
|
8063
8088
|
] });
|
|
8064
8089
|
}
|
|
8065
8090
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleToastNotificationComponent, decorators: [{
|
|
8066
8091
|
type: Component,
|
|
8067
|
-
args: [{ selector: 'cide-ele-toast-notification', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
8068
|
-
<!-- Toast Container -->
|
|
8069
|
-
<div class="toast-container">
|
|
8070
|
-
<div
|
|
8071
|
-
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
8072
|
-
class="toast-notification"
|
|
8073
|
-
[class]="getToastClass(notification)"
|
|
8074
|
-
[@toastAnimation]>
|
|
8075
|
-
|
|
8076
|
-
<!-- Toast Content -->
|
|
8077
|
-
<div class="toast-content">
|
|
8078
|
-
<!-- Icon -->
|
|
8079
|
-
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
8080
|
-
<cide-ele-icon size="sm">
|
|
8081
|
-
{{ notification.icon }}
|
|
8082
|
-
</cide-ele-icon>
|
|
8083
|
-
</div>
|
|
8084
|
-
|
|
8085
|
-
<!-- Text Content -->
|
|
8086
|
-
<div class="toast-text">
|
|
8087
|
-
<div *ngIf="notification.title" class="toast-title">
|
|
8088
|
-
{{ notification.title }}
|
|
8089
|
-
</div>
|
|
8090
|
-
<div class="toast-message">
|
|
8091
|
-
{{ notification.message }}
|
|
8092
|
-
</div>
|
|
8093
|
-
</div>
|
|
8094
|
-
|
|
8095
|
-
<!-- Close Button -->
|
|
8096
|
-
<button
|
|
8097
|
-
type="button"
|
|
8098
|
-
class="toast-close"
|
|
8099
|
-
(click)="removeNotification(notification.id)"
|
|
8100
|
-
aria-label="Close notification">
|
|
8101
|
-
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
8102
|
-
</button>
|
|
8103
|
-
</div>
|
|
8104
|
-
|
|
8105
|
-
<!-- Undo Button -->
|
|
8106
|
-
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
8107
|
-
<button
|
|
8108
|
-
type="button"
|
|
8109
|
-
class="toast-undo-btn"
|
|
8110
|
-
(click)="executeUndo(notification.id)">
|
|
8111
|
-
{{ notification.undoText }}
|
|
8112
|
-
</button>
|
|
8113
|
-
</div>
|
|
8114
|
-
|
|
8115
|
-
<!-- Progress Bar -->
|
|
8116
|
-
<div
|
|
8117
|
-
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
8118
|
-
class="toast-progress"
|
|
8119
|
-
[style.animation-duration]="notification.duration + 'ms'">
|
|
8120
|
-
</div>
|
|
8121
|
-
|
|
8122
|
-
<!-- Custom Progress Bar -->
|
|
8123
|
-
<div
|
|
8124
|
-
*ngIf="notification.showProgress"
|
|
8125
|
-
class="toast-custom-progress">
|
|
8126
|
-
<div
|
|
8127
|
-
class="toast-custom-progress-bar"
|
|
8128
|
-
[style.width.%]="notification.progress || 0">
|
|
8129
|
-
</div>
|
|
8130
|
-
</div>
|
|
8131
|
-
</div>
|
|
8132
|
-
</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>
|
|
8133
8158
|
`, animations: [
|
|
8134
|
-
|
|
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
|
+
])
|
|
8135
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"] }]
|
|
8136
8172
|
}] });
|
|
8137
8173
|
|
|
@@ -10023,7 +10059,7 @@ class CideEleDataGridComponent {
|
|
|
10023
10059
|
return this.mergedConfig().scroll;
|
|
10024
10060
|
}
|
|
10025
10061
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDataGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10026
|
-
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-1.5 tw-border-b tw-border-gray-200\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <!-- Left Side: Search Input and Action Icons -->\n <div class=\"tw-flex tw-items-center tw-space-x-1.5\">\n <!-- Search Input - Apple Style -->\n <div class=\"tw-max-w-md data-grid-search-input\">\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 size=\"sm\">\n </cide-ele-input>\n </div>\n \n <!-- Action Icons (Filter, Sort, Download) - Apple Style Compact -->\n <div class=\"tw-flex tw-items-center tw-space-x-1 data-grid-action-buttons\">\n <!-- Filter Button -->\n <button type=\"button\"\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\"\n title=\"Filter\"\n (click)=\"onActionClick(null, { key: 'filter', label: 'Filter', icon: 'filter_list', variant: 'ghost', onClick: 'filter' })\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\n </button>\n \n <!-- Sort Button -->\n <button type=\"button\"\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\"\n title=\"Sort\"\n (click)=\"onActionClick(null, { key: 'sort', label: 'Sort', icon: 'swap_vert', variant: 'ghost', onClick: 'sort' })\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">swap_vert</cide-ele-icon>\n </button>\n \n <!-- Download/Export Button -->\n <button type=\"button\"\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\"\n title=\"Download\"\n (click)=\"onActionClick(null, { key: 'download', label: 'Download', icon: 'file_download', variant: 'ghost', onClick: 'download' })\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\n </button>\n </div>\n </div>\n \n <!-- Right Side: 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 visibleColumns(); 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-relative\"\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 <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\n <span class=\"tw-truncate tw-flex-1\">{{ column.header }}</span>\n \n <!-- Active Filter Indicator -->\n @if (isColumnFiltered(column.key)) {\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\">\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-mr-0.5\">filter_alt</cide-ele-icon>\n {{ getActiveFilterCount(column.key) }}\n </div>\n }\n \n <!-- Column Menu Trigger (Three Dots Icon) -->\n @if (mergedConfig().columnMenu?.enabled) {\n <button\n type=\"button\"\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\"\n [class.tw-text-blue-600]=\"isColumnMenuOpen(column.key) || isColumnFiltered(column.key)\"\n [class.tw-bg-blue-50]=\"isColumnFiltered(column.key)\"\n (click)=\"toggleColumnMenu(column.key, $event)\"\n title=\"Column options\">\n <cide-ele-icon class=\"tw-w-5 tw-h-4\">more_vert</cide-ele-icon>\n </button>\n }\n </div>\n\n <!-- Column Menu Dropdown -->\n @if (isColumnMenuOpen(column.key)) {\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\">\n <div class=\"tw-py-1\">\n <!-- Sort Options -->\n @if (mergedConfig().columnMenu?.showSort && column.sortable !== false) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnSort(column, 'asc')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\n Ascending\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnSort(column, 'desc')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\n Descending\n </button>\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n }\n\n <!-- Filter Option -->\n @if (mergedConfig().columnMenu?.showFilter && column.filterable !== false) {\n <!-- Check if there's a custom filter renderer template -->\n @if (column.filterRenderer && templateRenderers[column.filterRenderer]) {\n <div class=\"tw-px-4 tw-py-2\">\n <ng-container [ngTemplateOutlet]=\"$any(templateRenderers[column.filterRenderer])\"\n [ngTemplateOutletContext]=\"{ $implicit: column, column: column, onFilter: onColumnFilter.bind(this) }\">\n </ng-container>\n </div>\n } @else {\n <!-- Excel-style Filter Button -->\n <button\n type=\"button\"\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\"\n [class.tw-bg-blue-50]=\"isFilterPanelOpen(column.key)\"\n (click)=\"toggleFilterPanel(column.key, $event)\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">filter_list</cide-ele-icon>\n Filter\n </div>\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-gray-400\">\n {{ isFilterPanelOpen(column.key) ? 'expand_less' : 'expand_more' }}\n </cide-ele-icon>\n </button>\n \n <!-- Excel-style Filter Panel -->\n @if (isFilterPanelOpen(column.key)) {\n <div class=\"tw-px-2 tw-py-2 tw-bg-gray-50\" (click)=\"$event.stopPropagation()\">\n <!-- Search box -->\n <div class=\"tw-px-2 tw-mb-2\">\n <input\n type=\"text\"\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\"\n placeholder=\"Search...\"\n [(ngModel)]=\"filterSearchTerm\"\n (click)=\"$event.stopPropagation()\">\n </div>\n \n <!-- Select All / Deselect All -->\n <div class=\"tw-px-2 tw-mb-1 tw-flex tw-gap-2\">\n <button\n type=\"button\"\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\n (click)=\"selectAllFilterValues(column); $event.stopPropagation()\">\n Select All\n </button>\n <span class=\"tw-text-xs tw-text-gray-400\">|</span>\n <button\n type=\"button\"\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\n (click)=\"deselectAllFilterValues(column); $event.stopPropagation()\">\n Clear\n </button>\n </div>\n \n <!-- Filter values list -->\n <div class=\"tw-max-h-48 tw-overflow-y-auto\">\n @for (item of getUniqueColumnValues(column); track item.value) {\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\">\n <input\n type=\"checkbox\"\n class=\"tw-mr-2 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\n [checked]=\"item.checked\"\n (change)=\"toggleFilterValue(column, item.value, $any($event.target).checked)\"\n (click)=\"$event.stopPropagation()\">\n <span class=\"tw-flex-1 tw-truncate\">{{ item.label }}</span>\n <span class=\"tw-text-gray-400 tw-ml-1\">({{ item.count }})</span>\n </label>\n }\n </div>\n </div>\n }\n }\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n }\n\n <!-- Autosize Option -->\n @if (mergedConfig().columnMenu?.showAutosize) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAutosize(column)\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">fit_screen</cide-ele-icon>\n Autosize\n </button>\n }\n\n <!-- Group By Column Option -->\n @if (mergedConfig().columnMenu?.showGroupBy && column.groupable !== false) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnGroupBy(column)\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">group_work</cide-ele-icon>\n Group By Column\n </button>\n }\n\n <!-- Manage Columns Option -->\n @if (mergedConfig().columnMenu?.showManageColumns) {\n <button\n type=\"button\"\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\"\n (click)=\"onManageColumns()\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">view_column</cide-ele-icon>\n Manage Columns\n </button>\n }\n\n <!-- Reset Columns Option -->\n @if (mergedConfig().columnMenu?.showResetColumns) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnReset()\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">refresh</cide-ele-icon>\n Reset Columns\n </button>\n }\n\n <!-- Hide Column Option -->\n @if (mergedConfig().columnMenu?.showHideColumn && column.hideable !== false) {\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnHide(column)\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">visibility_off</cide-ele-icon>\n Hide Column\n </button>\n }\n\n <!-- Aggregation Select Option -->\n @if (mergedConfig().columnMenu?.showAggregation && column.aggregatable !== false) {\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n <div class=\"tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase\">Aggregation</div>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'sum')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">functions</cide-ele-icon>\n Sum\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'avg')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">analytics</cide-ele-icon>\n Average\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'count')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">tag</cide-ele-icon>\n Count\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'min')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\n Min\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'max')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\n Max\n </button>\n }\n </div>\n </div>\n }\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-0 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-1 sm:tw-space-y-0\">\n \n <!-- Results Info -->\n @if (paginationConfig.showPageInfo) {\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\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 <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-500\">view_list</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\n <div class=\"tw-w-16 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=\"xs\"\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-2\">\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 type=\"button\"\n (click)=\"onPageChange(1)\"\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\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\"\n title=\"First page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">first_page</cide-ele-icon>\n </button>\n \n <!-- Previous Page -->\n <button\n type=\"button\"\n (click)=\"previousPage()\"\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\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\"\n title=\"Previous page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_left</cide-ele-icon>\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 type=\"button\"\n (click)=\"onPageChange(page)\"\n [disabled]=\"isRefreshing()\"\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\"\n [ngClass]=\"{\n 'tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700': currentPage() === page,\n 'tw-bg-white tw-text-gray-700 tw-border tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\n }\"\n [title]=\"'Page ' + page\">\n {{ page }}\n </button>\n }\n }\n \n <!-- Next Page -->\n <button\n type=\"button\"\n (click)=\"nextPage()\"\n [disabled]=\"!hasNextPage() || isRefreshing()\"\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\"\n title=\"Next page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_right</cide-ele-icon>\n </button>\n \n <!-- Last Page -->\n <button\n type=\"button\"\n (click)=\"onPageChange(totalPages())\"\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\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\"\n title=\"Last page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">last_page</cide-ele-icon>\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-1 tw-border-l tw-border-gray-200 tw-pl-2\">\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-16\">\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 size=\"xs\"\n (keydown.enter)=\"onJumpToPage()\">\n </cide-ele-input>\n </div>\n <button\n type=\"button\"\n (click)=\"onJumpToPage()\"\n [disabled]=\"isRefreshing()\"\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\"\n title=\"Go to page\">\n Go\n </button>\n </div>\n }\n \n <!-- Refresh Button -->\n @if (paginationConfig.showRefresh) {\n <button\n type=\"button\"\n (click)=\"onRefresh()\"\n [disabled]=\"isRefreshing()\"\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\"\n title=\"Refresh\">\n @if (isRefreshing()) {\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4 tw-animate-spin\">refresh</cide-ele-icon>\n } @else {\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">refresh</cide-ele-icon>\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;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"] }] });
|
|
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"] }] });
|
|
10027
10063
|
}
|
|
10028
10064
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDataGridComponent, decorators: [{
|
|
10029
10065
|
type: Component,
|
|
@@ -10035,7 +10071,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
10035
10071
|
CideSelectComponent,
|
|
10036
10072
|
CideIconComponent,
|
|
10037
10073
|
CideEleButtonComponent
|
|
10038
|
-
], 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-1.5 tw-border-b tw-border-gray-200\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <!-- Left Side: Search Input and Action Icons -->\n <div class=\"tw-flex tw-items-center tw-space-x-1.5\">\n <!-- Search Input - Apple Style -->\n <div class=\"tw-max-w-md data-grid-search-input\">\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 size=\"sm\">\n </cide-ele-input>\n </div>\n \n <!-- Action Icons (Filter, Sort, Download) - Apple Style Compact -->\n <div class=\"tw-flex tw-items-center tw-space-x-1 data-grid-action-buttons\">\n <!-- Filter Button -->\n <button type=\"button\"\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\"\n title=\"Filter\"\n (click)=\"onActionClick(null, { key: 'filter', label: 'Filter', icon: 'filter_list', variant: 'ghost', onClick: 'filter' })\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\n </button>\n \n <!-- Sort Button -->\n <button type=\"button\"\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\"\n title=\"Sort\"\n (click)=\"onActionClick(null, { key: 'sort', label: 'Sort', icon: 'swap_vert', variant: 'ghost', onClick: 'sort' })\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">swap_vert</cide-ele-icon>\n </button>\n \n <!-- Download/Export Button -->\n <button type=\"button\"\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\"\n title=\"Download\"\n (click)=\"onActionClick(null, { key: 'download', label: 'Download', icon: 'file_download', variant: 'ghost', onClick: 'download' })\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\n </button>\n </div>\n </div>\n \n <!-- Right Side: 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 visibleColumns(); 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-relative\"\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 <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\n <span class=\"tw-truncate tw-flex-1\">{{ column.header }}</span>\n \n <!-- Active Filter Indicator -->\n @if (isColumnFiltered(column.key)) {\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\">\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-mr-0.5\">filter_alt</cide-ele-icon>\n {{ getActiveFilterCount(column.key) }}\n </div>\n }\n \n <!-- Column Menu Trigger (Three Dots Icon) -->\n @if (mergedConfig().columnMenu?.enabled) {\n <button\n type=\"button\"\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\"\n [class.tw-text-blue-600]=\"isColumnMenuOpen(column.key) || isColumnFiltered(column.key)\"\n [class.tw-bg-blue-50]=\"isColumnFiltered(column.key)\"\n (click)=\"toggleColumnMenu(column.key, $event)\"\n title=\"Column options\">\n <cide-ele-icon class=\"tw-w-5 tw-h-4\">more_vert</cide-ele-icon>\n </button>\n }\n </div>\n\n <!-- Column Menu Dropdown -->\n @if (isColumnMenuOpen(column.key)) {\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\">\n <div class=\"tw-py-1\">\n <!-- Sort Options -->\n @if (mergedConfig().columnMenu?.showSort && column.sortable !== false) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnSort(column, 'asc')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\n Ascending\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnSort(column, 'desc')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\n Descending\n </button>\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n }\n\n <!-- Filter Option -->\n @if (mergedConfig().columnMenu?.showFilter && column.filterable !== false) {\n <!-- Check if there's a custom filter renderer template -->\n @if (column.filterRenderer && templateRenderers[column.filterRenderer]) {\n <div class=\"tw-px-4 tw-py-2\">\n <ng-container [ngTemplateOutlet]=\"$any(templateRenderers[column.filterRenderer])\"\n [ngTemplateOutletContext]=\"{ $implicit: column, column: column, onFilter: onColumnFilter.bind(this) }\">\n </ng-container>\n </div>\n } @else {\n <!-- Excel-style Filter Button -->\n <button\n type=\"button\"\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\"\n [class.tw-bg-blue-50]=\"isFilterPanelOpen(column.key)\"\n (click)=\"toggleFilterPanel(column.key, $event)\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">filter_list</cide-ele-icon>\n Filter\n </div>\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-gray-400\">\n {{ isFilterPanelOpen(column.key) ? 'expand_less' : 'expand_more' }}\n </cide-ele-icon>\n </button>\n \n <!-- Excel-style Filter Panel -->\n @if (isFilterPanelOpen(column.key)) {\n <div class=\"tw-px-2 tw-py-2 tw-bg-gray-50\" (click)=\"$event.stopPropagation()\">\n <!-- Search box -->\n <div class=\"tw-px-2 tw-mb-2\">\n <input\n type=\"text\"\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\"\n placeholder=\"Search...\"\n [(ngModel)]=\"filterSearchTerm\"\n (click)=\"$event.stopPropagation()\">\n </div>\n \n <!-- Select All / Deselect All -->\n <div class=\"tw-px-2 tw-mb-1 tw-flex tw-gap-2\">\n <button\n type=\"button\"\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\n (click)=\"selectAllFilterValues(column); $event.stopPropagation()\">\n Select All\n </button>\n <span class=\"tw-text-xs tw-text-gray-400\">|</span>\n <button\n type=\"button\"\n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-700\"\n (click)=\"deselectAllFilterValues(column); $event.stopPropagation()\">\n Clear\n </button>\n </div>\n \n <!-- Filter values list -->\n <div class=\"tw-max-h-48 tw-overflow-y-auto\">\n @for (item of getUniqueColumnValues(column); track item.value) {\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\">\n <input\n type=\"checkbox\"\n class=\"tw-mr-2 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\n [checked]=\"item.checked\"\n (change)=\"toggleFilterValue(column, item.value, $any($event.target).checked)\"\n (click)=\"$event.stopPropagation()\">\n <span class=\"tw-flex-1 tw-truncate\">{{ item.label }}</span>\n <span class=\"tw-text-gray-400 tw-ml-1\">({{ item.count }})</span>\n </label>\n }\n </div>\n </div>\n }\n }\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n }\n\n <!-- Autosize Option -->\n @if (mergedConfig().columnMenu?.showAutosize) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAutosize(column)\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">fit_screen</cide-ele-icon>\n Autosize\n </button>\n }\n\n <!-- Group By Column Option -->\n @if (mergedConfig().columnMenu?.showGroupBy && column.groupable !== false) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnGroupBy(column)\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">group_work</cide-ele-icon>\n Group By Column\n </button>\n }\n\n <!-- Manage Columns Option -->\n @if (mergedConfig().columnMenu?.showManageColumns) {\n <button\n type=\"button\"\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\"\n (click)=\"onManageColumns()\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">view_column</cide-ele-icon>\n Manage Columns\n </button>\n }\n\n <!-- Reset Columns Option -->\n @if (mergedConfig().columnMenu?.showResetColumns) {\n <button\n type=\"button\"\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\"\n (click)=\"onColumnReset()\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">refresh</cide-ele-icon>\n Reset Columns\n </button>\n }\n\n <!-- Hide Column Option -->\n @if (mergedConfig().columnMenu?.showHideColumn && column.hideable !== false) {\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnHide(column)\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">visibility_off</cide-ele-icon>\n Hide Column\n </button>\n }\n\n <!-- Aggregation Select Option -->\n @if (mergedConfig().columnMenu?.showAggregation && column.aggregatable !== false) {\n <div class=\"tw-border-t tw-border-gray-100 tw-my-1\"></div>\n <div class=\"tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase\">Aggregation</div>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'sum')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">functions</cide-ele-icon>\n Sum\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'avg')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">analytics</cide-ele-icon>\n Average\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'count')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">tag</cide-ele-icon>\n Count\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'min')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_downward</cide-ele-icon>\n Min\n </button>\n <button\n type=\"button\"\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\"\n (click)=\"onColumnAggregation(column, 'max')\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3 tw-text-gray-400\">arrow_upward</cide-ele-icon>\n Max\n </button>\n }\n </div>\n </div>\n }\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-0 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-1 sm:tw-space-y-0\">\n \n <!-- Results Info -->\n @if (paginationConfig.showPageInfo) {\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\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 <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-500\">view_list</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\n <div class=\"tw-w-16 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=\"xs\"\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-2\">\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 type=\"button\"\n (click)=\"onPageChange(1)\"\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\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\"\n title=\"First page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">first_page</cide-ele-icon>\n </button>\n \n <!-- Previous Page -->\n <button\n type=\"button\"\n (click)=\"previousPage()\"\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\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\"\n title=\"Previous page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_left</cide-ele-icon>\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 type=\"button\"\n (click)=\"onPageChange(page)\"\n [disabled]=\"isRefreshing()\"\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\"\n [ngClass]=\"{\n 'tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700': currentPage() === page,\n 'tw-bg-white tw-text-gray-700 tw-border tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\n }\"\n [title]=\"'Page ' + page\">\n {{ page }}\n </button>\n }\n }\n \n <!-- Next Page -->\n <button\n type=\"button\"\n (click)=\"nextPage()\"\n [disabled]=\"!hasNextPage() || isRefreshing()\"\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\"\n title=\"Next page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">chevron_right</cide-ele-icon>\n </button>\n \n <!-- Last Page -->\n <button\n type=\"button\"\n (click)=\"onPageChange(totalPages())\"\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\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\"\n title=\"Last page\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">last_page</cide-ele-icon>\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-1 tw-border-l tw-border-gray-200 tw-pl-2\">\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-16\">\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 size=\"xs\"\n (keydown.enter)=\"onJumpToPage()\">\n </cide-ele-input>\n </div>\n <button\n type=\"button\"\n (click)=\"onJumpToPage()\"\n [disabled]=\"isRefreshing()\"\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\"\n title=\"Go to page\">\n Go\n </button>\n </div>\n }\n \n <!-- Refresh Button -->\n @if (paginationConfig.showRefresh) {\n <button\n type=\"button\"\n (click)=\"onRefresh()\"\n [disabled]=\"isRefreshing()\"\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\"\n title=\"Refresh\">\n @if (isRefreshing()) {\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4 tw-animate-spin\">refresh</cide-ele-icon>\n } @else {\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">refresh</cide-ele-icon>\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;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"] }]
|
|
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"] }]
|
|
10039
10075
|
}], propDecorators: { config: [{
|
|
10040
10076
|
type: Input
|
|
10041
10077
|
}], templateRenderers: [{
|
|
@@ -10384,170 +10420,170 @@ class CideEleDropdownComponent {
|
|
|
10384
10420
|
this.dropdownManager.unregisterDropdown(this.dropdownId);
|
|
10385
10421
|
}
|
|
10386
10422
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10387
|
-
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: `
|
|
10388
|
-
<div class="tw-relative" #dropdownContainer>
|
|
10389
|
-
<!-- Trigger Button -->
|
|
10390
|
-
<button
|
|
10391
|
-
type="button"
|
|
10392
|
-
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"
|
|
10393
|
-
[class]="getTriggerClasses()"
|
|
10394
|
-
(click)="toggleDropdown($event)"
|
|
10395
|
-
[title]="config.triggerIcon === 'more_vert' ? 'More actions' : 'Options'">
|
|
10396
|
-
|
|
10397
|
-
<!-- Custom Trigger Template -->
|
|
10398
|
-
@if (triggerTemplate) {
|
|
10399
|
-
<ng-container *ngTemplateOutlet="triggerTemplate; context: { $implicit: isOpen() }">
|
|
10400
|
-
</ng-container>
|
|
10401
|
-
} @else {
|
|
10402
|
-
<!-- Default Trigger -->
|
|
10403
|
-
<cide-ele-icon
|
|
10404
|
-
[class]="getTriggerIconClasses()"
|
|
10405
|
-
[size]="config.triggerSize || 'sm'">
|
|
10406
|
-
{{ config.triggerIcon || 'more_vert' }}
|
|
10407
|
-
</cide-ele-icon>
|
|
10408
|
-
}
|
|
10409
|
-
</button>
|
|
10410
|
-
|
|
10411
|
-
<!-- Dropdown Menu (Non-Portal) -->
|
|
10412
|
-
@if (isOpen() && !getSafeConfig().usePortal) {
|
|
10413
|
-
<div
|
|
10414
|
-
#dropdownMenu
|
|
10415
|
-
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"
|
|
10416
|
-
[class]="getMenuClasses()"
|
|
10417
|
-
[style]="getMenuStyles()"
|
|
10418
|
-
role="menu">
|
|
10419
|
-
<ng-container *ngTemplateOutlet="dropdownMenuTemplate; context: { $implicit: items }">
|
|
10420
|
-
</ng-container>
|
|
10421
|
-
</div>
|
|
10422
|
-
}
|
|
10423
|
-
</div>
|
|
10424
|
-
|
|
10425
|
-
<!-- Dropdown Menu Template (for both portal and non-portal) -->
|
|
10426
|
-
<ng-template #dropdownMenuTemplate let-context="context">
|
|
10427
|
-
<!-- Custom Menu Template -->
|
|
10428
|
-
@if (menuTemplate) {
|
|
10429
|
-
<ng-container *ngTemplateOutlet="menuTemplate; context: { $implicit: context?.items }">
|
|
10430
|
-
</ng-container>
|
|
10431
|
-
} @else {
|
|
10432
|
-
<!-- Default Menu -->
|
|
10433
|
-
<div class="tw-py-1" role="none">
|
|
10434
|
-
@for (item of context?.items; track item.id) {
|
|
10435
|
-
@if (item.divider) {
|
|
10436
|
-
<!-- Divider Item -->
|
|
10437
|
-
<hr class="tw-border-t tw-border-gray-200 tw-my-1" role="separator">
|
|
10438
|
-
} @else {
|
|
10439
|
-
<!-- Regular Menu Item -->
|
|
10440
|
-
<button
|
|
10441
|
-
type="button"
|
|
10442
|
-
[class]="getItemClasses(item)"
|
|
10443
|
-
[disabled]="item.disabled"
|
|
10444
|
-
(click)="onItemClick(item, $event)"
|
|
10445
|
-
[attr.data-item-id]="item.id">
|
|
10446
|
-
|
|
10447
|
-
<!-- Item Icon -->
|
|
10448
|
-
@if (item.icon) {
|
|
10449
|
-
<cide-ele-icon
|
|
10450
|
-
class="tw-w-4 tw-h-4 tw-mr-2"
|
|
10451
|
-
[class]="item.iconColor || 'tw-text-gray-400'"
|
|
10452
|
-
size="xs">
|
|
10453
|
-
{{ item.icon }}
|
|
10454
|
-
</cide-ele-icon>
|
|
10455
|
-
}
|
|
10456
|
-
|
|
10457
|
-
<!-- Item Label -->
|
|
10458
|
-
<span [class]="item.textColor || 'tw-text-gray-700'">
|
|
10459
|
-
{{ item.label }}
|
|
10460
|
-
</span>
|
|
10461
|
-
</button>
|
|
10462
|
-
}
|
|
10463
|
-
}
|
|
10464
|
-
</div>
|
|
10465
|
-
}
|
|
10466
|
-
</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>
|
|
10467
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"] }] });
|
|
10468
10504
|
}
|
|
10469
10505
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDropdownComponent, decorators: [{
|
|
10470
10506
|
type: Component,
|
|
10471
|
-
args: [{ selector: 'cide-ele-dropdown', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
10472
|
-
<div class="tw-relative" #dropdownContainer>
|
|
10473
|
-
<!-- Trigger Button -->
|
|
10474
|
-
<button
|
|
10475
|
-
type="button"
|
|
10476
|
-
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"
|
|
10477
|
-
[class]="getTriggerClasses()"
|
|
10478
|
-
(click)="toggleDropdown($event)"
|
|
10479
|
-
[title]="config.triggerIcon === 'more_vert' ? 'More actions' : 'Options'">
|
|
10480
|
-
|
|
10481
|
-
<!-- Custom Trigger Template -->
|
|
10482
|
-
@if (triggerTemplate) {
|
|
10483
|
-
<ng-container *ngTemplateOutlet="triggerTemplate; context: { $implicit: isOpen() }">
|
|
10484
|
-
</ng-container>
|
|
10485
|
-
} @else {
|
|
10486
|
-
<!-- Default Trigger -->
|
|
10487
|
-
<cide-ele-icon
|
|
10488
|
-
[class]="getTriggerIconClasses()"
|
|
10489
|
-
[size]="config.triggerSize || 'sm'">
|
|
10490
|
-
{{ config.triggerIcon || 'more_vert' }}
|
|
10491
|
-
</cide-ele-icon>
|
|
10492
|
-
}
|
|
10493
|
-
</button>
|
|
10494
|
-
|
|
10495
|
-
<!-- Dropdown Menu (Non-Portal) -->
|
|
10496
|
-
@if (isOpen() && !getSafeConfig().usePortal) {
|
|
10497
|
-
<div
|
|
10498
|
-
#dropdownMenu
|
|
10499
|
-
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"
|
|
10500
|
-
[class]="getMenuClasses()"
|
|
10501
|
-
[style]="getMenuStyles()"
|
|
10502
|
-
role="menu">
|
|
10503
|
-
<ng-container *ngTemplateOutlet="dropdownMenuTemplate; context: { $implicit: items }">
|
|
10504
|
-
</ng-container>
|
|
10505
|
-
</div>
|
|
10506
|
-
}
|
|
10507
|
-
</div>
|
|
10508
|
-
|
|
10509
|
-
<!-- Dropdown Menu Template (for both portal and non-portal) -->
|
|
10510
|
-
<ng-template #dropdownMenuTemplate let-context="context">
|
|
10511
|
-
<!-- Custom Menu Template -->
|
|
10512
|
-
@if (menuTemplate) {
|
|
10513
|
-
<ng-container *ngTemplateOutlet="menuTemplate; context: { $implicit: context?.items }">
|
|
10514
|
-
</ng-container>
|
|
10515
|
-
} @else {
|
|
10516
|
-
<!-- Default Menu -->
|
|
10517
|
-
<div class="tw-py-1" role="none">
|
|
10518
|
-
@for (item of context?.items; track item.id) {
|
|
10519
|
-
@if (item.divider) {
|
|
10520
|
-
<!-- Divider Item -->
|
|
10521
|
-
<hr class="tw-border-t tw-border-gray-200 tw-my-1" role="separator">
|
|
10522
|
-
} @else {
|
|
10523
|
-
<!-- Regular Menu Item -->
|
|
10524
|
-
<button
|
|
10525
|
-
type="button"
|
|
10526
|
-
[class]="getItemClasses(item)"
|
|
10527
|
-
[disabled]="item.disabled"
|
|
10528
|
-
(click)="onItemClick(item, $event)"
|
|
10529
|
-
[attr.data-item-id]="item.id">
|
|
10530
|
-
|
|
10531
|
-
<!-- Item Icon -->
|
|
10532
|
-
@if (item.icon) {
|
|
10533
|
-
<cide-ele-icon
|
|
10534
|
-
class="tw-w-4 tw-h-4 tw-mr-2"
|
|
10535
|
-
[class]="item.iconColor || 'tw-text-gray-400'"
|
|
10536
|
-
size="xs">
|
|
10537
|
-
{{ item.icon }}
|
|
10538
|
-
</cide-ele-icon>
|
|
10539
|
-
}
|
|
10540
|
-
|
|
10541
|
-
<!-- Item Label -->
|
|
10542
|
-
<span [class]="item.textColor || 'tw-text-gray-700'">
|
|
10543
|
-
{{ item.label }}
|
|
10544
|
-
</span>
|
|
10545
|
-
</button>
|
|
10546
|
-
}
|
|
10547
|
-
}
|
|
10548
|
-
</div>
|
|
10549
|
-
}
|
|
10550
|
-
</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>
|
|
10551
10587
|
`, styles: [".dropdown-menu{transform-origin:top right}\n"] }]
|
|
10552
10588
|
}], propDecorators: { items: [{
|
|
10553
10589
|
type: Input
|
|
@@ -10745,222 +10781,222 @@ class CideEleFloatingContainerComponent {
|
|
|
10745
10781
|
return 'linear-gradient(135deg, var(--cide-theme-primary-color) 0%, var(--tw-blue-400) 100%)';
|
|
10746
10782
|
}
|
|
10747
10783
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10748
|
-
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: `
|
|
10749
|
-
<div
|
|
10750
|
-
#container
|
|
10751
|
-
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"
|
|
10752
|
-
[style.left.px]="isMaximized() ? 20 : position().x"
|
|
10753
|
-
[style.top.px]="isMaximized() ? 20 : position().y"
|
|
10754
|
-
[style.width]="isMaximized() ? 'calc(100vw - 40px)' : (config().width || '400px')"
|
|
10755
|
-
[style.height]="isMaximized() ? 'calc(100vh - 40px)' : (isMinimized() ? 'auto' : (savedHeight || config().height || '500px'))"
|
|
10756
|
-
[style.min-width]="config().minWidth || '300px'"
|
|
10757
|
-
[style.min-height]="isMinimized() ? 'auto' : (config().minHeight || '200px')"
|
|
10758
|
-
[style.max-width]="config().maxWidth || '90vw'"
|
|
10759
|
-
[style.max-height]="config().maxHeight || '90vh'"
|
|
10760
|
-
[style.z-index]="zIndex()"
|
|
10761
|
-
[class.tw-cursor-move]="isDragging()"
|
|
10762
|
-
[class.tw-select-none]="true"
|
|
10763
|
-
(mousedown)="onMouseDown($event)"
|
|
10764
|
-
(mousemove)="onMouseMove($event)"
|
|
10765
|
-
(mouseup)="onMouseUp($event)"
|
|
10766
|
-
(mouseleave)="onMouseUp($event)">
|
|
10767
|
-
|
|
10768
|
-
<!-- Header -->
|
|
10769
|
-
<div
|
|
10770
|
-
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"
|
|
10771
|
-
[style.background]="getHeaderBackground()"
|
|
10772
|
-
(mousedown)="startDrag($event)"
|
|
10773
|
-
(click)="onHeaderClick($event)">
|
|
10774
|
-
|
|
10775
|
-
<!-- Title Section -->
|
|
10776
|
-
<div class="tw-flex tw-items-center tw-space-x-2 tw-text-white">
|
|
10777
|
-
@if (config().icon) {
|
|
10778
|
-
<cide-ele-icon class="tw-w-4 tw-h-4">{{ config().icon }}</cide-ele-icon>
|
|
10779
|
-
}
|
|
10780
|
-
<h2 class="tw-text-sm tw-font-medium tw-truncate">{{ config().title }}</h2>
|
|
10781
|
-
@if (isMinimized()) {
|
|
10782
|
-
<span class="tw-text-xs tw-opacity-75">(Click to restore)</span>
|
|
10783
|
-
}
|
|
10784
|
-
</div>
|
|
10785
|
-
|
|
10786
|
-
<!-- Action Buttons -->
|
|
10787
|
-
<div class="tw-flex tw-items-center tw-space-x-1">
|
|
10788
|
-
@if (config().minimizable !== false) {
|
|
10789
|
-
<button
|
|
10790
|
-
cideEleButton
|
|
10791
|
-
variant="ghost"
|
|
10792
|
-
size="xs"
|
|
10793
|
-
type="button"
|
|
10794
|
-
(click)="minimize(); $event.stopPropagation()"
|
|
10795
|
-
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"
|
|
10796
|
-
[title]="isMinimized() ? 'Restore' : 'Minimize'">
|
|
10797
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMinimized() ? 'open_in_full' : 'remove' }}</cide-ele-icon>
|
|
10798
|
-
</button>
|
|
10799
|
-
}
|
|
10800
|
-
@if (config().maximizable !== false) {
|
|
10801
|
-
<button
|
|
10802
|
-
cideEleButton
|
|
10803
|
-
variant="ghost"
|
|
10804
|
-
size="xs"
|
|
10805
|
-
type="button"
|
|
10806
|
-
(click)="toggleMaximize(); $event.stopPropagation()"
|
|
10807
|
-
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"
|
|
10808
|
-
[title]="isMaximized() ? 'Restore' : 'Maximize'">
|
|
10809
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMaximized() ? 'fullscreen_exit' : 'fullscreen' }}</cide-ele-icon>
|
|
10810
|
-
</button>
|
|
10811
|
-
}
|
|
10812
|
-
@if (config().closable !== false) {
|
|
10813
|
-
<button
|
|
10814
|
-
cideEleButton
|
|
10815
|
-
variant="ghost"
|
|
10816
|
-
size="xs"
|
|
10817
|
-
type="button"
|
|
10818
|
-
(click)="close(); $event.stopPropagation()"
|
|
10819
|
-
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"
|
|
10820
|
-
title="Close">
|
|
10821
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">close</cide-ele-icon>
|
|
10822
|
-
</button>
|
|
10823
|
-
}
|
|
10824
|
-
</div>
|
|
10825
|
-
</div>
|
|
10826
|
-
|
|
10827
|
-
<!-- Content with @defer for performance -->
|
|
10828
|
-
@if (!isMinimized()) {
|
|
10829
|
-
<div
|
|
10830
|
-
class="tw-flex-1 tw-overflow-auto tw-overflow-x-hidden floating-container-content tw-bg-gray-50 tw-bg-opacity-90"
|
|
10831
|
-
[style.height]="isMaximized() ? 'calc(100vh - 100px)' : 'auto'"
|
|
10832
|
-
[style.min-height]="'200px'">
|
|
10833
|
-
@defer (when !isMinimized() && isVisible()) {
|
|
10834
|
-
<div class="tw-p-0 tw-min-h-full">
|
|
10835
|
-
<ng-content></ng-content>
|
|
10836
|
-
</div>
|
|
10837
|
-
} @placeholder {
|
|
10838
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4 tw-min-h-[200px]">
|
|
10839
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10840
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading content...</span>
|
|
10841
|
-
</div>
|
|
10842
|
-
}
|
|
10843
|
-
</div>
|
|
10844
|
-
}
|
|
10845
|
-
|
|
10846
|
-
<!-- Resize Handle (if resizable) -->
|
|
10847
|
-
@if (config().resizable !== false) {
|
|
10848
|
-
<div
|
|
10849
|
-
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"
|
|
10850
|
-
(mousedown)="startResize($event)">
|
|
10851
|
-
</div>
|
|
10852
|
-
}
|
|
10853
|
-
</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>
|
|
10854
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"] }] });
|
|
10855
10891
|
}
|
|
10856
10892
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerComponent, decorators: [{
|
|
10857
10893
|
type: Component,
|
|
10858
|
-
args: [{ selector: 'cide-ele-floating-container', standalone: true, imports: [CommonModule, CideEleButtonComponent, CideIconComponent, CideSpinnerComponent], template: `
|
|
10859
|
-
<div
|
|
10860
|
-
#container
|
|
10861
|
-
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"
|
|
10862
|
-
[style.left.px]="isMaximized() ? 20 : position().x"
|
|
10863
|
-
[style.top.px]="isMaximized() ? 20 : position().y"
|
|
10864
|
-
[style.width]="isMaximized() ? 'calc(100vw - 40px)' : (config().width || '400px')"
|
|
10865
|
-
[style.height]="isMaximized() ? 'calc(100vh - 40px)' : (isMinimized() ? 'auto' : (savedHeight || config().height || '500px'))"
|
|
10866
|
-
[style.min-width]="config().minWidth || '300px'"
|
|
10867
|
-
[style.min-height]="isMinimized() ? 'auto' : (config().minHeight || '200px')"
|
|
10868
|
-
[style.max-width]="config().maxWidth || '90vw'"
|
|
10869
|
-
[style.max-height]="config().maxHeight || '90vh'"
|
|
10870
|
-
[style.z-index]="zIndex()"
|
|
10871
|
-
[class.tw-cursor-move]="isDragging()"
|
|
10872
|
-
[class.tw-select-none]="true"
|
|
10873
|
-
(mousedown)="onMouseDown($event)"
|
|
10874
|
-
(mousemove)="onMouseMove($event)"
|
|
10875
|
-
(mouseup)="onMouseUp($event)"
|
|
10876
|
-
(mouseleave)="onMouseUp($event)">
|
|
10877
|
-
|
|
10878
|
-
<!-- Header -->
|
|
10879
|
-
<div
|
|
10880
|
-
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"
|
|
10881
|
-
[style.background]="getHeaderBackground()"
|
|
10882
|
-
(mousedown)="startDrag($event)"
|
|
10883
|
-
(click)="onHeaderClick($event)">
|
|
10884
|
-
|
|
10885
|
-
<!-- Title Section -->
|
|
10886
|
-
<div class="tw-flex tw-items-center tw-space-x-2 tw-text-white">
|
|
10887
|
-
@if (config().icon) {
|
|
10888
|
-
<cide-ele-icon class="tw-w-4 tw-h-4">{{ config().icon }}</cide-ele-icon>
|
|
10889
|
-
}
|
|
10890
|
-
<h2 class="tw-text-sm tw-font-medium tw-truncate">{{ config().title }}</h2>
|
|
10891
|
-
@if (isMinimized()) {
|
|
10892
|
-
<span class="tw-text-xs tw-opacity-75">(Click to restore)</span>
|
|
10893
|
-
}
|
|
10894
|
-
</div>
|
|
10895
|
-
|
|
10896
|
-
<!-- Action Buttons -->
|
|
10897
|
-
<div class="tw-flex tw-items-center tw-space-x-1">
|
|
10898
|
-
@if (config().minimizable !== false) {
|
|
10899
|
-
<button
|
|
10900
|
-
cideEleButton
|
|
10901
|
-
variant="ghost"
|
|
10902
|
-
size="xs"
|
|
10903
|
-
type="button"
|
|
10904
|
-
(click)="minimize(); $event.stopPropagation()"
|
|
10905
|
-
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"
|
|
10906
|
-
[title]="isMinimized() ? 'Restore' : 'Minimize'">
|
|
10907
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMinimized() ? 'open_in_full' : 'remove' }}</cide-ele-icon>
|
|
10908
|
-
</button>
|
|
10909
|
-
}
|
|
10910
|
-
@if (config().maximizable !== false) {
|
|
10911
|
-
<button
|
|
10912
|
-
cideEleButton
|
|
10913
|
-
variant="ghost"
|
|
10914
|
-
size="xs"
|
|
10915
|
-
type="button"
|
|
10916
|
-
(click)="toggleMaximize(); $event.stopPropagation()"
|
|
10917
|
-
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"
|
|
10918
|
-
[title]="isMaximized() ? 'Restore' : 'Maximize'">
|
|
10919
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">{{ isMaximized() ? 'fullscreen_exit' : 'fullscreen' }}</cide-ele-icon>
|
|
10920
|
-
</button>
|
|
10921
|
-
}
|
|
10922
|
-
@if (config().closable !== false) {
|
|
10923
|
-
<button
|
|
10924
|
-
cideEleButton
|
|
10925
|
-
variant="ghost"
|
|
10926
|
-
size="xs"
|
|
10927
|
-
type="button"
|
|
10928
|
-
(click)="close(); $event.stopPropagation()"
|
|
10929
|
-
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"
|
|
10930
|
-
title="Close">
|
|
10931
|
-
<cide-ele-icon class="tw-w-[1.38rem] tw-h-3">close</cide-ele-icon>
|
|
10932
|
-
</button>
|
|
10933
|
-
}
|
|
10934
|
-
</div>
|
|
10935
|
-
</div>
|
|
10936
|
-
|
|
10937
|
-
<!-- Content with @defer for performance -->
|
|
10938
|
-
@if (!isMinimized()) {
|
|
10939
|
-
<div
|
|
10940
|
-
class="tw-flex-1 tw-overflow-auto tw-overflow-x-hidden floating-container-content tw-bg-gray-50 tw-bg-opacity-90"
|
|
10941
|
-
[style.height]="isMaximized() ? 'calc(100vh - 100px)' : 'auto'"
|
|
10942
|
-
[style.min-height]="'200px'">
|
|
10943
|
-
@defer (when !isMinimized() && isVisible()) {
|
|
10944
|
-
<div class="tw-p-0 tw-min-h-full">
|
|
10945
|
-
<ng-content></ng-content>
|
|
10946
|
-
</div>
|
|
10947
|
-
} @placeholder {
|
|
10948
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4 tw-min-h-[200px]">
|
|
10949
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
10950
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading content...</span>
|
|
10951
|
-
</div>
|
|
10952
|
-
}
|
|
10953
|
-
</div>
|
|
10954
|
-
}
|
|
10955
|
-
|
|
10956
|
-
<!-- Resize Handle (if resizable) -->
|
|
10957
|
-
@if (config().resizable !== false) {
|
|
10958
|
-
<div
|
|
10959
|
-
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"
|
|
10960
|
-
(mousedown)="startResize($event)">
|
|
10961
|
-
</div>
|
|
10962
|
-
}
|
|
10963
|
-
</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>
|
|
10964
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"] }]
|
|
10965
11001
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
10966
11002
|
type: Input
|
|
@@ -10990,96 +11026,96 @@ class CideEleFloatingContainerManagerComponent {
|
|
|
10990
11026
|
// Computed properties
|
|
10991
11027
|
visibleContainers = computed(() => this.containerService.visibleContainers(), ...(ngDevMode ? [{ debugName: "visibleContainers" }] : []));
|
|
10992
11028
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerManagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10993
|
-
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: `
|
|
10994
|
-
@for (container of visibleContainers(); track container.id) {
|
|
10995
|
-
<cide-ele-floating-container
|
|
10996
|
-
[config]="containerService.getConfigSignal(container.config)"
|
|
10997
|
-
[isMinimized]="containerService.getMinimizedSignal(container.id)"
|
|
10998
|
-
[isMaximized]="containerService.getMaximizedSignal(container.id)"
|
|
10999
|
-
[isVisible]="containerService.getVisibleSignal(container.id)"
|
|
11000
|
-
[zIndex]="containerService.getZIndexSignal(container.id)"
|
|
11001
|
-
(closeEvent)="containerService.onClose($event)"
|
|
11002
|
-
(minimizeEvent)="containerService.onMinimize($event)"
|
|
11003
|
-
(maximizeEvent)="containerService.onMaximize($event)"
|
|
11004
|
-
(clickEvent)="containerService.bringToFront($event)">
|
|
11005
|
-
|
|
11006
|
-
<!-- Dynamic content loading with @defer for performance -->
|
|
11007
|
-
@defer (when container.isVisible) {
|
|
11008
|
-
<div
|
|
11009
|
-
cideEleFloatingDynamic
|
|
11010
|
-
[componentId]="container.config.componentId || container.config.id"
|
|
11011
|
-
[componentConfig]="containerService.getComponentConfig(container.config)"
|
|
11012
|
-
[isVisible]="container.isVisible">
|
|
11013
|
-
</div>
|
|
11014
|
-
} @placeholder {
|
|
11015
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4">
|
|
11016
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
11017
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading component...</span>
|
|
11018
|
-
</div>
|
|
11019
|
-
} @error {
|
|
11020
|
-
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-p-4 tw-text-center">
|
|
11021
|
-
<cide-ele-icon class="tw-w-8 tw-h-8 tw-text-red-500 tw-mb-2">error</cide-ele-icon>
|
|
11022
|
-
<p class="tw-text-red-600 tw-font-medium">Failed to load component</p>
|
|
11023
|
-
<p class="tw-text-gray-500 tw-text-sm">Component "{{ container.config.componentId || container.config.id }}" is not available</p>
|
|
11024
|
-
<button
|
|
11025
|
-
cideEleButton
|
|
11026
|
-
variant="outline"
|
|
11027
|
-
size="xs"
|
|
11028
|
-
(click)="containerService.onClose(container.id)"
|
|
11029
|
-
class="tw-mt-2">
|
|
11030
|
-
Close
|
|
11031
|
-
</button>
|
|
11032
|
-
</div>
|
|
11033
|
-
}
|
|
11034
|
-
</cide-ele-floating-container>
|
|
11035
|
-
}
|
|
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
|
+
}
|
|
11036
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)]] });
|
|
11037
11073
|
}
|
|
11038
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: [{
|
|
11039
11075
|
type: Component,
|
|
11040
|
-
args: [{ selector: 'cide-ele-floating-container-manager', standalone: true, imports: [CommonModule, CideEleFloatingContainerComponent, CideEleFloatingContainerDynamicDirective, CideSpinnerComponent, CideEleButtonComponent, CideIconComponent], template: `
|
|
11041
|
-
@for (container of visibleContainers(); track container.id) {
|
|
11042
|
-
<cide-ele-floating-container
|
|
11043
|
-
[config]="containerService.getConfigSignal(container.config)"
|
|
11044
|
-
[isMinimized]="containerService.getMinimizedSignal(container.id)"
|
|
11045
|
-
[isMaximized]="containerService.getMaximizedSignal(container.id)"
|
|
11046
|
-
[isVisible]="containerService.getVisibleSignal(container.id)"
|
|
11047
|
-
[zIndex]="containerService.getZIndexSignal(container.id)"
|
|
11048
|
-
(closeEvent)="containerService.onClose($event)"
|
|
11049
|
-
(minimizeEvent)="containerService.onMinimize($event)"
|
|
11050
|
-
(maximizeEvent)="containerService.onMaximize($event)"
|
|
11051
|
-
(clickEvent)="containerService.bringToFront($event)">
|
|
11052
|
-
|
|
11053
|
-
<!-- Dynamic content loading with @defer for performance -->
|
|
11054
|
-
@defer (when container.isVisible) {
|
|
11055
|
-
<div
|
|
11056
|
-
cideEleFloatingDynamic
|
|
11057
|
-
[componentId]="container.config.componentId || container.config.id"
|
|
11058
|
-
[componentConfig]="containerService.getComponentConfig(container.config)"
|
|
11059
|
-
[isVisible]="container.isVisible">
|
|
11060
|
-
</div>
|
|
11061
|
-
} @placeholder {
|
|
11062
|
-
<div class="tw-flex tw-items-center tw-justify-center tw-p-4">
|
|
11063
|
-
<cide-ele-spinner size="sm"></cide-ele-spinner>
|
|
11064
|
-
<span class="tw-ml-2 tw-text-sm tw-text-gray-600">Loading component...</span>
|
|
11065
|
-
</div>
|
|
11066
|
-
} @error {
|
|
11067
|
-
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-p-4 tw-text-center">
|
|
11068
|
-
<cide-ele-icon class="tw-w-8 tw-h-8 tw-text-red-500 tw-mb-2">error</cide-ele-icon>
|
|
11069
|
-
<p class="tw-text-red-600 tw-font-medium">Failed to load component</p>
|
|
11070
|
-
<p class="tw-text-gray-500 tw-text-sm">Component "{{ container.config.componentId || container.config.id }}" is not available</p>
|
|
11071
|
-
<button
|
|
11072
|
-
cideEleButton
|
|
11073
|
-
variant="outline"
|
|
11074
|
-
size="xs"
|
|
11075
|
-
(click)="containerService.onClose(container.id)"
|
|
11076
|
-
class="tw-mt-2">
|
|
11077
|
-
Close
|
|
11078
|
-
</button>
|
|
11079
|
-
</div>
|
|
11080
|
-
}
|
|
11081
|
-
</cide-ele-floating-container>
|
|
11082
|
-
}
|
|
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
|
+
}
|
|
11083
11119
|
` }]
|
|
11084
11120
|
}], ctorParameters: null, propDecorators: null }) });
|
|
11085
11121
|
|