cloud-ide-element 1.1.93 → 1.1.95
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 +1221 -1137
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +37 -3
- package/package.json +1 -1
|
@@ -2797,7 +2797,7 @@ class CideSelectComponent {
|
|
|
2797
2797
|
multi: true,
|
|
2798
2798
|
useExisting: forwardRef(() => CideSelectComponent),
|
|
2799
2799
|
}
|
|
2800
|
-
], 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]=\"isDisabled()\" (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': isDisabled(),\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 'cide-select-tree-option': context?.treeView?.enabled\n }\" class=\"cide-select-option\" [style.white-space]=\"context?.treeView?.enabled ? 'pre' : 'normal'\" [style.font-family]=\"context?.treeView?.enabled ? 'monospace' : 'inherit'\">\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 }] });
|
|
2800
|
+
], 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-leading-icon': leadingIcon(),\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]=\"isDisabled()\" (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': isDisabled(),\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 'cide-select-tree-option': context?.treeView?.enabled\n }\" class=\"cide-select-option\" [style.white-space]=\"context?.treeView?.enabled ? 'pre' : 'normal'\" [style.font-family]=\"context?.treeView?.enabled ? 'monospace' : 'inherit'\">\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 }] });
|
|
2801
2801
|
}
|
|
2802
2802
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSelectComponent, decorators: [{
|
|
2803
2803
|
type: Component,
|
|
@@ -2812,7 +2812,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
2812
2812
|
multi: true,
|
|
2813
2813
|
useExisting: forwardRef(() => CideSelectComponent),
|
|
2814
2814
|
}
|
|
2815
|
-
], 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]=\"isDisabled()\" (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': isDisabled(),\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 'cide-select-tree-option': context?.treeView?.enabled\n }\" class=\"cide-select-option\" [style.white-space]=\"context?.treeView?.enabled ? 'pre' : 'normal'\" [style.font-family]=\"context?.treeView?.enabled ? 'monospace' : 'inherit'\">\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"] }]
|
|
2815
|
+
], 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-leading-icon': leadingIcon(),\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]=\"isDisabled()\" (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': isDisabled(),\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 'cide-select-tree-option': context?.treeView?.enabled\n }\" class=\"cide-select-option\" [style.white-space]=\"context?.treeView?.enabled ? 'pre' : 'normal'\" [style.font-family]=\"context?.treeView?.enabled ? 'monospace' : 'inherit'\">\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"] }]
|
|
2816
2816
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { optionComponents: [{
|
|
2817
2817
|
type: ContentChildren,
|
|
2818
2818
|
args: [CideSelectOptionComponent]
|
|
@@ -4319,11 +4319,13 @@ class CideEleFileInputComponent {
|
|
|
4319
4319
|
groupId = signal(null, ...(ngDevMode ? [{ debugName: "groupId" }] : [])); // Group ID for multiple file uploads
|
|
4320
4320
|
isMultipleUploadMode = signal(false, ...(ngDevMode ? [{ debugName: "isMultipleUploadMode" }] : [])); // Flag to track if we're in multiple upload mode
|
|
4321
4321
|
hasEverUploaded = signal(false, ...(ngDevMode ? [{ debugName: "hasEverUploaded" }] : [])); // Track if this component has ever uploaded files
|
|
4322
|
+
failedFile = signal(null, ...(ngDevMode ? [{ debugName: "failedFile" }] : [])); // Store failed file for retry
|
|
4322
4323
|
// Computed signals for better relationships
|
|
4323
4324
|
hasFiles = computed(() => this.files() !== null && this.files().length > 0, ...(ngDevMode ? [{ debugName: "hasFiles" }] : []));
|
|
4324
4325
|
canUpload = computed(() => this.hasFiles() && !this.isUploading() && !this.disabledSignal(), ...(ngDevMode ? [{ debugName: "canUpload" }] : []));
|
|
4325
4326
|
isInErrorState = computed(() => this.uploadStatus() === 'error', ...(ngDevMode ? [{ debugName: "isInErrorState" }] : []));
|
|
4326
4327
|
isInSuccessState = computed(() => this.uploadStatus() === 'success', ...(ngDevMode ? [{ debugName: "isInSuccessState" }] : []));
|
|
4328
|
+
canRetry = computed(() => this.isInErrorState() && this.failedFile() !== null && !this.isUploading() && !this.disabledSignal(), ...(ngDevMode ? [{ debugName: "canRetry" }] : []));
|
|
4327
4329
|
// Optimized computed values - only calculate when needed
|
|
4328
4330
|
totalFileSize = computed(() => {
|
|
4329
4331
|
const files = this.files();
|
|
@@ -4465,10 +4467,27 @@ class CideEleFileInputComponent {
|
|
|
4465
4467
|
this.fileNames.set([]);
|
|
4466
4468
|
this.clearPreviews();
|
|
4467
4469
|
this.uploadStatus.set('idle');
|
|
4470
|
+
this.failedFile.set(null); // Clear failed file when clearing files
|
|
4468
4471
|
console.log('🔄 [FileInput] Upload status reset to:', this.uploadStatus());
|
|
4469
4472
|
this.onChange(null);
|
|
4470
4473
|
this.fileChange.emit(null);
|
|
4471
4474
|
}
|
|
4475
|
+
/**
|
|
4476
|
+
* Retry uploading the failed file
|
|
4477
|
+
*/
|
|
4478
|
+
retryUpload() {
|
|
4479
|
+
const failedFile = this.failedFile();
|
|
4480
|
+
if (!failedFile) {
|
|
4481
|
+
console.warn('⚠️ [FileInput] No failed file to retry');
|
|
4482
|
+
return;
|
|
4483
|
+
}
|
|
4484
|
+
console.log('🔄 [FileInput] Retrying upload for file:', failedFile.name);
|
|
4485
|
+
// Clear the failed file reference (will be set again if retry fails)
|
|
4486
|
+
this.failedFile.set(null);
|
|
4487
|
+
// Reset upload status and retry
|
|
4488
|
+
this.uploadStatus.set('idle');
|
|
4489
|
+
this.uploadFile(failedFile);
|
|
4490
|
+
}
|
|
4472
4491
|
uploadFile(file) {
|
|
4473
4492
|
console.log('🚨 [FileInput] uploadFile called for:', file.name, '- SINGLE UPLOAD INITIATED');
|
|
4474
4493
|
// Angular 20: Use PendingTasks for better loading state management
|
|
@@ -4547,6 +4566,8 @@ class CideEleFileInputComponent {
|
|
|
4547
4566
|
const uploadedId = response?.data?.core_file_manager?.[0]?.cyfm_id;
|
|
4548
4567
|
if (uploadedId) {
|
|
4549
4568
|
console.log('✅ [FileInput] File uploaded successfully with ID:', uploadedId);
|
|
4569
|
+
// Clear failed file on successful upload
|
|
4570
|
+
this.failedFile.set(null);
|
|
4550
4571
|
// Set the uploaded ID as the form control value
|
|
4551
4572
|
this.onChange(uploadedId);
|
|
4552
4573
|
console.log('📝 [FileInput] Form control value set to uploaded ID:', uploadedId);
|
|
@@ -4571,6 +4592,9 @@ class CideEleFileInputComponent {
|
|
|
4571
4592
|
// Angular 20: Complete the pending task even on error
|
|
4572
4593
|
// this.pendingTasks.complete(uploadTask); // TODO: Fix PendingTasks API usage
|
|
4573
4594
|
// console.log('❌ [FileInput] Pending task completed for failed upload');
|
|
4595
|
+
// Store the failed file for retry
|
|
4596
|
+
this.failedFile.set(file);
|
|
4597
|
+
console.log('💾 [FileInput] Failed file stored for retry:', file.name);
|
|
4574
4598
|
// Set upload status to 'error' and remove upload validation error
|
|
4575
4599
|
this.uploadStatus.set('error');
|
|
4576
4600
|
console.log('🔄 [FileInput] Upload status set to:', this.uploadStatus());
|
|
@@ -5293,7 +5317,7 @@ class CideEleFileInputComponent {
|
|
|
5293
5317
|
useExisting: CideEleFileInputComponent,
|
|
5294
5318
|
multi: true
|
|
5295
5319
|
}
|
|
5296
|
-
], 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"] }] });
|
|
5320
|
+
], 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 @if (isInErrorState() && failedFile()) {\r\n <button \r\n type=\"button\" \r\n class=\"tw-absolute tw-bottom-2 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed tw-shadow-lg tw-z-10\"\r\n (click)=\"retryUpload(); $event.stopPropagation()\"\r\n [disabled]=\"isUploading() || disabledSignal()\"\r\n title=\"Retry upload\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\r\n Retry Upload\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 <!-- Error message and retry button for preview box mode -->\r\n @if (isInErrorState() && failedFile() && !hasImages()) {\r\n <div class=\"tw-mt-2 tw-flex tw-flex-col tw-items-center tw-gap-2\">\r\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\" \r\n (click)=\"retryUpload()\"\r\n [disabled]=\"isUploading() || disabledSignal()\">\r\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\r\n Retry Upload\r\n </button>\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 (isInErrorState() && failedFile()) {\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\" \r\n (click)=\"retryUpload(); $event.stopPropagation()\"\r\n [disabled]=\"isUploading() || disabledSignal()\"\r\n title=\"Retry upload\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\r\n Retry\r\n </button>\r\n }\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 (isInErrorState() && failedFile() && !errorTextSignal()) {\r\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-mt-1\">\r\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\" \r\n (click)=\"retryUpload()\"\r\n [disabled]=\"isUploading() || disabledSignal()\">\r\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\r\n Retry Upload\r\n </button>\r\n </div>\r\n }\r\n @if (helperTextSignal() && !errorTextSignal() && !isInErrorState()) {\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"] }] });
|
|
5297
5321
|
}
|
|
5298
5322
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
5299
5323
|
type: Component,
|
|
@@ -5308,7 +5332,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
5308
5332
|
useExisting: CideEleFileInputComponent,
|
|
5309
5333
|
multi: true
|
|
5310
5334
|
}
|
|
5311
|
-
], 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> " }]
|
|
5335
|
+
], 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 @if (isInErrorState() && failedFile()) {\r\n <button \r\n type=\"button\" \r\n class=\"tw-absolute tw-bottom-2 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed tw-shadow-lg tw-z-10\"\r\n (click)=\"retryUpload(); $event.stopPropagation()\"\r\n [disabled]=\"isUploading() || disabledSignal()\"\r\n title=\"Retry upload\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\r\n Retry Upload\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 <!-- Error message and retry button for preview box mode -->\r\n @if (isInErrorState() && failedFile() && !hasImages()) {\r\n <div class=\"tw-mt-2 tw-flex tw-flex-col tw-items-center tw-gap-2\">\r\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\" \r\n (click)=\"retryUpload()\"\r\n [disabled]=\"isUploading() || disabledSignal()\">\r\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\r\n Retry Upload\r\n </button>\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 (isInErrorState() && failedFile()) {\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\" \r\n (click)=\"retryUpload(); $event.stopPropagation()\"\r\n [disabled]=\"isUploading() || disabledSignal()\"\r\n title=\"Retry upload\">\r\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\r\n Retry\r\n </button>\r\n }\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 (isInErrorState() && failedFile() && !errorTextSignal()) {\r\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-mt-1\">\r\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\r\n <button type=\"button\" \r\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\" \r\n (click)=\"retryUpload()\"\r\n [disabled]=\"isUploading() || disabledSignal()\">\r\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\r\n Retry Upload\r\n </button>\r\n </div>\r\n }\r\n @if (helperTextSignal() && !errorTextSignal() && !isInErrorState()) {\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> " }]
|
|
5312
5336
|
}], ctorParameters: () => [], propDecorators: { label: [{
|
|
5313
5337
|
type: Input
|
|
5314
5338
|
}], accept: [{
|
|
@@ -7380,1230 +7404,1287 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
7380
7404
|
}] } });
|
|
7381
7405
|
|
|
7382
7406
|
/**
|
|
7383
|
-
*
|
|
7384
|
-
*
|
|
7407
|
+
* CIDE Element Library - Theme Service
|
|
7408
|
+
* Independent theme management for the element library
|
|
7409
|
+
* Can be used standalone without parent application theme dependencies
|
|
7385
7410
|
*/
|
|
7386
|
-
class
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7411
|
+
class CideThemeService {
|
|
7412
|
+
platformId = inject(PLATFORM_ID);
|
|
7413
|
+
isBrowser;
|
|
7414
|
+
currentTheme$ = new BehaviorSubject('light');
|
|
7415
|
+
effectiveTheme$ = new BehaviorSubject('light');
|
|
7416
|
+
config = {
|
|
7417
|
+
theme: 'light',
|
|
7418
|
+
targetElement: null,
|
|
7419
|
+
persistPreference: true,
|
|
7420
|
+
storageKey: 'cide-theme',
|
|
7421
|
+
useDataAttribute: true,
|
|
7422
|
+
useClassName: true
|
|
7423
|
+
};
|
|
7424
|
+
constructor() {
|
|
7425
|
+
this.isBrowser = isPlatformBrowser(this.platformId);
|
|
7426
|
+
if (this.isBrowser) {
|
|
7427
|
+
this.config.targetElement = document.documentElement;
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7392
7430
|
/**
|
|
7393
|
-
*
|
|
7431
|
+
* Initialize the theme service with configuration
|
|
7432
|
+
* This should be called once in the application, typically in main.ts or app config
|
|
7394
7433
|
*/
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7434
|
+
initialize(config) {
|
|
7435
|
+
if (!this.isBrowser) {
|
|
7436
|
+
return;
|
|
7437
|
+
}
|
|
7438
|
+
// Merge configuration
|
|
7439
|
+
if (config) {
|
|
7440
|
+
this.config = {
|
|
7441
|
+
...this.config,
|
|
7442
|
+
...config,
|
|
7443
|
+
targetElement: config.targetElement || document.documentElement
|
|
7444
|
+
};
|
|
7445
|
+
}
|
|
7446
|
+
// Load saved theme preference
|
|
7447
|
+
const savedTheme = this.loadThemePreference();
|
|
7448
|
+
const initialTheme = savedTheme || this.config.theme;
|
|
7449
|
+
// Apply initial theme
|
|
7450
|
+
this.setTheme(initialTheme);
|
|
7451
|
+
// Listen for system theme changes if theme is set to 'auto'
|
|
7452
|
+
if (initialTheme === 'auto') {
|
|
7453
|
+
this.listenToSystemTheme();
|
|
7404
7454
|
}
|
|
7405
|
-
// Debug: Log the URL being used
|
|
7406
|
-
console.log('[NotificationApi] Request URL:', this.apiUrl);
|
|
7407
|
-
console.log('[NotificationApi] Full URL with params:', `${this.apiUrl}?${httpParams.toString()}`);
|
|
7408
|
-
return this.http.get(this.apiUrl, { params: httpParams });
|
|
7409
7455
|
}
|
|
7410
7456
|
/**
|
|
7411
|
-
*
|
|
7457
|
+
* Set the current theme
|
|
7412
7458
|
*/
|
|
7413
|
-
|
|
7414
|
-
|
|
7459
|
+
setTheme(theme) {
|
|
7460
|
+
if (!this.isBrowser) {
|
|
7461
|
+
return;
|
|
7462
|
+
}
|
|
7463
|
+
this.currentTheme$.next(theme);
|
|
7464
|
+
// Save preference if persistence is enabled
|
|
7465
|
+
if (this.config.persistPreference) {
|
|
7466
|
+
this.saveThemePreference(theme);
|
|
7467
|
+
}
|
|
7468
|
+
// Determine effective theme
|
|
7469
|
+
let effectiveTheme;
|
|
7470
|
+
if (theme === 'auto') {
|
|
7471
|
+
effectiveTheme = this.getSystemTheme();
|
|
7472
|
+
this.listenToSystemTheme();
|
|
7473
|
+
}
|
|
7474
|
+
else {
|
|
7475
|
+
effectiveTheme = theme;
|
|
7476
|
+
}
|
|
7477
|
+
this.effectiveTheme$.next(effectiveTheme);
|
|
7478
|
+
this.applyTheme(effectiveTheme);
|
|
7415
7479
|
}
|
|
7416
7480
|
/**
|
|
7417
|
-
* Get
|
|
7481
|
+
* Get the current theme preference
|
|
7418
7482
|
*/
|
|
7419
|
-
|
|
7420
|
-
return this.
|
|
7483
|
+
getCurrentTheme() {
|
|
7484
|
+
return this.currentTheme$.value;
|
|
7421
7485
|
}
|
|
7422
7486
|
/**
|
|
7423
|
-
*
|
|
7487
|
+
* Get the effective theme (actual theme being displayed)
|
|
7424
7488
|
*/
|
|
7425
|
-
|
|
7426
|
-
return this.
|
|
7489
|
+
getEffectiveTheme() {
|
|
7490
|
+
return this.effectiveTheme$.value;
|
|
7427
7491
|
}
|
|
7428
7492
|
/**
|
|
7429
|
-
*
|
|
7493
|
+
* Observable for current theme changes
|
|
7430
7494
|
*/
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
console.log('[NotificationApi] Mark as read URL:', url);
|
|
7434
|
-
return this.http.put(url, {});
|
|
7495
|
+
getCurrentTheme$() {
|
|
7496
|
+
return this.currentTheme$.asObservable();
|
|
7435
7497
|
}
|
|
7436
7498
|
/**
|
|
7437
|
-
*
|
|
7499
|
+
* Observable for effective theme changes
|
|
7438
7500
|
*/
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
console.log('[NotificationApi] Mark all as read URL:', url);
|
|
7442
|
-
return this.http.put(url, {});
|
|
7501
|
+
getEffectiveTheme$() {
|
|
7502
|
+
return this.effectiveTheme$.asObservable();
|
|
7443
7503
|
}
|
|
7444
7504
|
/**
|
|
7445
|
-
*
|
|
7505
|
+
* Toggle between light and dark themes
|
|
7446
7506
|
*/
|
|
7447
|
-
|
|
7448
|
-
|
|
7507
|
+
toggleTheme() {
|
|
7508
|
+
const currentEffective = this.effectiveTheme$.value;
|
|
7509
|
+
const newTheme = currentEffective === 'light' ? 'dark' : 'light';
|
|
7510
|
+
this.setTheme(newTheme);
|
|
7449
7511
|
}
|
|
7450
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NotificationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7451
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NotificationApiService, providedIn: 'root' });
|
|
7452
|
-
}
|
|
7453
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NotificationApiService, decorators: [{
|
|
7454
|
-
type: Injectable,
|
|
7455
|
-
args: [{
|
|
7456
|
-
providedIn: 'root'
|
|
7457
|
-
}]
|
|
7458
|
-
}] });
|
|
7459
|
-
|
|
7460
|
-
/**
|
|
7461
|
-
* WebSocket Notification Service
|
|
7462
|
-
* Socket.IO client for real-time notifications
|
|
7463
|
-
*/
|
|
7464
|
-
class WebSocketNotificationService {
|
|
7465
|
-
socket = null;
|
|
7466
|
-
notificationSubject = new Subject();
|
|
7467
|
-
connectionStatusSubject = new BehaviorSubject({
|
|
7468
|
-
connected: false
|
|
7469
|
-
});
|
|
7470
|
-
// Signals for reactive state
|
|
7471
|
-
notifications = signal([], ...(ngDevMode ? [{ debugName: "notifications" }] : []));
|
|
7472
|
-
unreadNotifications = signal([], ...(ngDevMode ? [{ debugName: "unreadNotifications" }] : []));
|
|
7473
|
-
// Public observables
|
|
7474
|
-
notification$ = this.notificationSubject.asObservable();
|
|
7475
|
-
connectionStatus$ = this.connectionStatusSubject.asObservable();
|
|
7476
|
-
// Computed signals
|
|
7477
|
-
allNotifications = computed(() => this.notifications(), ...(ngDevMode ? [{ debugName: "allNotifications" }] : []));
|
|
7478
|
-
unreadNotificationsCount = computed(() => this.unreadNotifications().length, ...(ngDevMode ? [{ debugName: "unreadNotificationsCount" }] : []));
|
|
7479
|
-
isConnected = computed(() => this.connectionStatusSubject.value.connected, ...(ngDevMode ? [{ debugName: "isConnected" }] : []));
|
|
7480
7512
|
/**
|
|
7481
|
-
*
|
|
7513
|
+
* Check if dark mode is currently active
|
|
7482
7514
|
*/
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
console.log('Socket already connected');
|
|
7486
|
-
return;
|
|
7487
|
-
}
|
|
7488
|
-
// Resolve actual API URL from placeholder
|
|
7489
|
-
// The placeholder is replaced by host interceptor for HTTP, but WebSocket needs direct resolution
|
|
7490
|
-
// Based on host-manager.json: "__cloud_ide_suite_layout__" -> "http://localhost:3001"
|
|
7491
|
-
const placeholder = hostManagerRoutesUrl.cideSuiteHost;
|
|
7492
|
-
let apiUrl = 'http://localhost:3001'; // Default from host-manager.json
|
|
7493
|
-
// Try to resolve from host-manager.json via fetch (same as HTTP interceptor)
|
|
7494
|
-
if (typeof window !== 'undefined') {
|
|
7495
|
-
// Try to fetch host-manager.json to get the actual URL
|
|
7496
|
-
fetch('/routes/host-manager.json')
|
|
7497
|
-
.then(res => res.json())
|
|
7498
|
-
.then((hostManagerRoutes) => {
|
|
7499
|
-
const hostManagerRoutesEntry = hostManagerRoutes?.hosts?.find((hostRoute) => placeholder?.includes(hostRoute?.url));
|
|
7500
|
-
if (hostManagerRoutesEntry?.replace) {
|
|
7501
|
-
apiUrl = hostManagerRoutesEntry.replace;
|
|
7502
|
-
}
|
|
7503
|
-
// Remove /api suffix if present (Socket.IO connects to base server)
|
|
7504
|
-
apiUrl = apiUrl.replace(/\/api\/?$/, '');
|
|
7505
|
-
this.connectWithUrl(apiUrl, token, userId);
|
|
7506
|
-
})
|
|
7507
|
-
.catch(() => {
|
|
7508
|
-
// Fallback if fetch fails - use default or try localStorage
|
|
7509
|
-
this.resolveUrlFallback(token, userId);
|
|
7510
|
-
});
|
|
7511
|
-
}
|
|
7512
|
-
else {
|
|
7513
|
-
// Server-side or fallback
|
|
7514
|
-
this.resolveUrlFallback(token, userId);
|
|
7515
|
-
}
|
|
7515
|
+
isDarkMode() {
|
|
7516
|
+
return this.effectiveTheme$.value === 'dark';
|
|
7516
7517
|
}
|
|
7517
7518
|
/**
|
|
7518
|
-
*
|
|
7519
|
+
* Apply theme to target element
|
|
7519
7520
|
*/
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7521
|
+
applyTheme(theme) {
|
|
7522
|
+
if (!this.isBrowser || !this.config.targetElement) {
|
|
7523
|
+
return;
|
|
7524
|
+
}
|
|
7525
|
+
const element = this.config.targetElement;
|
|
7526
|
+
// Apply data-theme attribute
|
|
7527
|
+
if (this.config.useDataAttribute) {
|
|
7528
|
+
element.setAttribute('data-theme', theme);
|
|
7529
|
+
}
|
|
7530
|
+
// Apply class name
|
|
7531
|
+
if (this.config.useClassName) {
|
|
7532
|
+
if (theme === 'dark') {
|
|
7533
|
+
element.classList.add('dark-mode');
|
|
7534
|
+
element.classList.remove('light-mode');
|
|
7529
7535
|
}
|
|
7530
7536
|
else {
|
|
7531
|
-
|
|
7532
|
-
|
|
7537
|
+
element.classList.add('light-mode');
|
|
7538
|
+
element.classList.remove('dark-mode');
|
|
7533
7539
|
}
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7540
|
+
}
|
|
7541
|
+
// Also apply to body for better compatibility
|
|
7542
|
+
if (element === document.documentElement && document.body) {
|
|
7543
|
+
if (this.config.useDataAttribute) {
|
|
7544
|
+
document.body.setAttribute('data-theme', theme);
|
|
7545
|
+
}
|
|
7546
|
+
if (this.config.useClassName) {
|
|
7547
|
+
if (theme === 'dark') {
|
|
7548
|
+
document.body.classList.add('dark-mode');
|
|
7549
|
+
document.body.classList.remove('light-mode');
|
|
7550
|
+
}
|
|
7551
|
+
else {
|
|
7552
|
+
document.body.classList.add('light-mode');
|
|
7553
|
+
document.body.classList.remove('dark-mode');
|
|
7554
|
+
}
|
|
7538
7555
|
}
|
|
7539
7556
|
}
|
|
7540
|
-
this.connectWithUrl(apiUrl, token, userId);
|
|
7541
7557
|
}
|
|
7542
7558
|
/**
|
|
7543
|
-
*
|
|
7559
|
+
* Get system theme preference
|
|
7544
7560
|
*/
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
reconnection: true,
|
|
7554
|
-
reconnectionDelay: 1000,
|
|
7555
|
-
reconnectionAttempts: 5,
|
|
7556
|
-
reconnectionDelayMax: 5000
|
|
7557
|
-
});
|
|
7558
|
-
this.setupEventHandlers(userId);
|
|
7561
|
+
getSystemTheme() {
|
|
7562
|
+
if (!this.isBrowser) {
|
|
7563
|
+
return 'light';
|
|
7564
|
+
}
|
|
7565
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
7566
|
+
return 'dark';
|
|
7567
|
+
}
|
|
7568
|
+
return 'light';
|
|
7559
7569
|
}
|
|
7560
7570
|
/**
|
|
7561
|
-
*
|
|
7571
|
+
* Listen to system theme changes
|
|
7562
7572
|
*/
|
|
7563
|
-
|
|
7564
|
-
if (!this.
|
|
7573
|
+
listenToSystemTheme() {
|
|
7574
|
+
if (!this.isBrowser || !window.matchMedia) {
|
|
7565
7575
|
return;
|
|
7566
|
-
// Connection established
|
|
7567
|
-
this.socket.on('connect', () => {
|
|
7568
|
-
console.log('Socket.IO connected:', this.socket?.id);
|
|
7569
|
-
this.connectionStatusSubject.next({
|
|
7570
|
-
connected: true,
|
|
7571
|
-
socketId: this.socket?.id,
|
|
7572
|
-
userId: userId,
|
|
7573
|
-
timestamp: new Date()
|
|
7574
|
-
});
|
|
7575
|
-
});
|
|
7576
|
-
// Connection confirmation
|
|
7577
|
-
this.socket.on('connected', (data) => {
|
|
7578
|
-
console.log('Notification service connected:', data);
|
|
7579
|
-
this.connectionStatusSubject.next({
|
|
7580
|
-
connected: true,
|
|
7581
|
-
socketId: data.socketId,
|
|
7582
|
-
userId: data.userId,
|
|
7583
|
-
timestamp: data.timestamp
|
|
7584
|
-
});
|
|
7585
|
-
});
|
|
7586
|
-
// Receive notification
|
|
7587
|
-
this.socket.on('notification', (notification) => {
|
|
7588
|
-
console.log('Received notification:', notification);
|
|
7589
|
-
// Add to notifications list
|
|
7590
|
-
this.notifications.update(notifications => [notification, ...notifications]);
|
|
7591
|
-
// Add to unread list
|
|
7592
|
-
this.unreadNotifications.update(unread => [notification, ...unread]);
|
|
7593
|
-
// Emit to subscribers
|
|
7594
|
-
this.notificationSubject.next(notification);
|
|
7595
|
-
// Acknowledge receipt
|
|
7596
|
-
this.acknowledgeNotification(notification.id);
|
|
7597
|
-
});
|
|
7598
|
-
// Disconnect
|
|
7599
|
-
this.socket.on('disconnect', (reason) => {
|
|
7600
|
-
console.log('Socket.IO disconnected:', reason);
|
|
7601
|
-
this.connectionStatusSubject.next({
|
|
7602
|
-
connected: false,
|
|
7603
|
-
timestamp: new Date()
|
|
7604
|
-
});
|
|
7605
|
-
});
|
|
7606
|
-
// Connection error
|
|
7607
|
-
this.socket.on('connect_error', (error) => {
|
|
7608
|
-
console.error('Socket.IO connection error:', error);
|
|
7609
|
-
this.connectionStatusSubject.next({
|
|
7610
|
-
connected: false,
|
|
7611
|
-
timestamp: new Date()
|
|
7612
|
-
});
|
|
7613
|
-
});
|
|
7614
|
-
}
|
|
7615
|
-
/**
|
|
7616
|
-
* Acknowledge notification receipt
|
|
7617
|
-
*/
|
|
7618
|
-
acknowledgeNotification(notificationId) {
|
|
7619
|
-
if (this.socket?.connected) {
|
|
7620
|
-
this.socket.emit('notification:ack', { notificationId });
|
|
7621
7576
|
}
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7577
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
7578
|
+
const handler = (e) => {
|
|
7579
|
+
if (this.currentTheme$.value === 'auto') {
|
|
7580
|
+
const effectiveTheme = e.matches ? 'dark' : 'light';
|
|
7581
|
+
this.effectiveTheme$.next(effectiveTheme);
|
|
7582
|
+
this.applyTheme(effectiveTheme);
|
|
7583
|
+
}
|
|
7584
|
+
};
|
|
7585
|
+
// Modern browsers
|
|
7586
|
+
if (mediaQuery.addEventListener) {
|
|
7587
|
+
mediaQuery.addEventListener('change', handler);
|
|
7588
|
+
}
|
|
7589
|
+
else if (mediaQuery.addListener) {
|
|
7590
|
+
// Legacy browsers
|
|
7591
|
+
mediaQuery.addListener(handler);
|
|
7631
7592
|
}
|
|
7632
7593
|
}
|
|
7633
7594
|
/**
|
|
7634
|
-
*
|
|
7635
|
-
*/
|
|
7636
|
-
markAllAsRead() {
|
|
7637
|
-
const unreadIds = this.unreadNotifications().map(n => n.id);
|
|
7638
|
-
unreadIds.forEach(id => this.markAsRead(id));
|
|
7639
|
-
}
|
|
7640
|
-
/**
|
|
7641
|
-
* Disconnect from server
|
|
7595
|
+
* Save theme preference to localStorage
|
|
7642
7596
|
*/
|
|
7643
|
-
|
|
7644
|
-
if (this.
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7597
|
+
saveThemePreference(theme) {
|
|
7598
|
+
if (!this.isBrowser) {
|
|
7599
|
+
return;
|
|
7600
|
+
}
|
|
7601
|
+
try {
|
|
7602
|
+
// Save in new format: 'light', 'dark', or 'auto'
|
|
7603
|
+
localStorage.setItem(this.config.storageKey, theme);
|
|
7604
|
+
// Also update old format for backward compatibility
|
|
7605
|
+
// This allows old code that reads 'true'/'false' to still work
|
|
7606
|
+
if (theme === 'dark') {
|
|
7607
|
+
localStorage.setItem(this.config.storageKey, 'dark');
|
|
7608
|
+
}
|
|
7609
|
+
else if (theme === 'light') {
|
|
7610
|
+
localStorage.setItem(this.config.storageKey, 'light');
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
catch (error) {
|
|
7614
|
+
console.warn('Failed to save theme preference to localStorage:', error);
|
|
7651
7615
|
}
|
|
7652
7616
|
}
|
|
7653
7617
|
/**
|
|
7654
|
-
*
|
|
7618
|
+
* Load theme preference from localStorage
|
|
7655
7619
|
*/
|
|
7656
|
-
|
|
7657
|
-
this.
|
|
7658
|
-
|
|
7620
|
+
loadThemePreference() {
|
|
7621
|
+
if (!this.isBrowser) {
|
|
7622
|
+
return null;
|
|
7623
|
+
}
|
|
7624
|
+
try {
|
|
7625
|
+
const saved = localStorage.getItem(this.config.storageKey);
|
|
7626
|
+
// Handle new format: 'light', 'dark', 'auto'
|
|
7627
|
+
if (saved && ['light', 'dark', 'auto'].includes(saved)) {
|
|
7628
|
+
return saved;
|
|
7629
|
+
}
|
|
7630
|
+
// Handle legacy format: 'true' (dark mode) or 'false' (light mode)
|
|
7631
|
+
if (saved === 'true') {
|
|
7632
|
+
// Migrate to new format
|
|
7633
|
+
localStorage.setItem(this.config.storageKey, 'dark');
|
|
7634
|
+
return 'dark';
|
|
7635
|
+
}
|
|
7636
|
+
else if (saved === 'false') {
|
|
7637
|
+
// Migrate to new format
|
|
7638
|
+
localStorage.setItem(this.config.storageKey, 'light');
|
|
7639
|
+
return 'light';
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
catch (error) {
|
|
7643
|
+
console.warn('Failed to load theme preference from localStorage:', error);
|
|
7644
|
+
}
|
|
7645
|
+
return null;
|
|
7659
7646
|
}
|
|
7660
7647
|
/**
|
|
7661
|
-
*
|
|
7648
|
+
* Clear saved theme preference
|
|
7662
7649
|
*/
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7650
|
+
clearThemePreference() {
|
|
7651
|
+
if (!this.isBrowser) {
|
|
7652
|
+
return;
|
|
7653
|
+
}
|
|
7654
|
+
try {
|
|
7655
|
+
localStorage.removeItem(this.config.storageKey);
|
|
7656
|
+
}
|
|
7657
|
+
catch (error) {
|
|
7658
|
+
console.warn('Failed to clear theme preference from localStorage:', error);
|
|
7659
|
+
}
|
|
7671
7660
|
}
|
|
7672
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
7673
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
7661
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7662
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideThemeService, providedIn: 'root' });
|
|
7674
7663
|
}
|
|
7675
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
7664
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideThemeService, decorators: [{
|
|
7676
7665
|
type: Injectable,
|
|
7677
7666
|
args: [{
|
|
7678
7667
|
providedIn: 'root'
|
|
7679
7668
|
}]
|
|
7680
|
-
}] });
|
|
7669
|
+
}], ctorParameters: () => [] });
|
|
7681
7670
|
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7671
|
+
/**
|
|
7672
|
+
* Apple-inspired dark/light mode toggle component
|
|
7673
|
+
*
|
|
7674
|
+
* Features:
|
|
7675
|
+
* - Smooth animated toggle switch
|
|
7676
|
+
* - Apple-style design with rounded corners and shadows
|
|
7677
|
+
* - Works on pages without header/sidebar (dynamic website, login pages, auth project)
|
|
7678
|
+
* - Automatically syncs with system theme preference
|
|
7679
|
+
* - Persists user preference to localStorage
|
|
7680
|
+
*/
|
|
7681
|
+
class CideEleThemeToggleComponent {
|
|
7682
|
+
themeService = inject(CideThemeService);
|
|
7683
|
+
destroyRef = inject(DestroyRef);
|
|
7684
|
+
// Current theme state
|
|
7685
|
+
isDarkMode = signal(false, ...(ngDevMode ? [{ debugName: "isDarkMode" }] : []));
|
|
7686
|
+
isAnimating = signal(false, ...(ngDevMode ? [{ debugName: "isAnimating" }] : []));
|
|
7687
|
+
// Computed values
|
|
7688
|
+
togglePosition = computed(() => this.isDarkMode() ? 'translate-x-5' : 'translate-x-0', ...(ngDevMode ? [{ debugName: "togglePosition" }] : []));
|
|
7689
|
+
toggleBgColor = computed(() => this.isDarkMode()
|
|
7690
|
+
? 'tw-bg-gradient-to-r tw-from-blue-500 tw-to-indigo-600'
|
|
7691
|
+
: 'tw-bg-gray-200 dark:tw-bg-gray-700', ...(ngDevMode ? [{ debugName: "toggleBgColor" }] : []));
|
|
7692
|
+
ngOnInit() {
|
|
7693
|
+
// Subscribe to theme changes
|
|
7694
|
+
this.themeService.getEffectiveTheme$()
|
|
7695
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7696
|
+
.subscribe(theme => {
|
|
7697
|
+
this.isDarkMode.set(theme === 'dark');
|
|
7698
|
+
});
|
|
7699
|
+
// Set initial state
|
|
7700
|
+
this.isDarkMode.set(this.themeService.isDarkMode());
|
|
7690
7701
|
}
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
*/
|
|
7694
|
-
getFileList(body) {
|
|
7695
|
-
const query = generateStringFromObject(body);
|
|
7696
|
-
return this.http?.get(cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]))
|
|
7697
|
-
.pipe(tap((response) => {
|
|
7698
|
-
if (response?.success) {
|
|
7699
|
-
this.fileListSubject.next(response?.data || []);
|
|
7700
|
-
}
|
|
7701
|
-
}), catchError$1(error => {
|
|
7702
|
-
console.error('CideCoreFileManagerService API error:', error);
|
|
7703
|
-
return this.handleError(error);
|
|
7704
|
-
}));
|
|
7702
|
+
ngOnDestroy() {
|
|
7703
|
+
// Cleanup handled by takeUntilDestroyed
|
|
7705
7704
|
}
|
|
7706
7705
|
/**
|
|
7707
|
-
*
|
|
7706
|
+
* Toggle between light and dark mode
|
|
7708
7707
|
*/
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7708
|
+
toggleTheme() {
|
|
7709
|
+
if (this.isAnimating()) {
|
|
7710
|
+
return; // Prevent rapid clicking
|
|
7711
|
+
}
|
|
7712
|
+
this.isAnimating.set(true);
|
|
7713
|
+
// Toggle theme using the service
|
|
7714
|
+
this.themeService.toggleTheme();
|
|
7715
|
+
// Reset animation flag after transition
|
|
7716
|
+
setTimeout(() => {
|
|
7717
|
+
this.isAnimating.set(false);
|
|
7718
|
+
}, 300);
|
|
7712
7719
|
}
|
|
7720
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleThemeToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7721
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleThemeToggleComponent, isStandalone: true, selector: "cide-ele-theme-toggle", ngImport: i0, template: "<div class=\"cide-theme-toggle-wrapper\">\r\n <button\r\n type=\"button\"\r\n class=\"cide-theme-toggle-button\"\r\n [class.is-dark]=\"isDarkMode()\"\r\n [class.is-animating]=\"isAnimating()\"\r\n (click)=\"toggleTheme()\"\r\n [attr.aria-label]=\"isDarkMode() ? 'Switch to light mode' : 'Switch to dark mode'\"\r\n [attr.aria-pressed]=\"isDarkMode()\">\r\n \r\n <!-- Toggle Track -->\r\n <span class=\"cide-theme-toggle-track\" [class]=\"toggleBgColor()\">\r\n <!-- Sun Icon (Light Mode) -->\r\n <span class=\"cide-theme-toggle-icon sun-icon\" [class.hidden]=\"isDarkMode()\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <circle cx=\"12\" cy=\"12\" r=\"4\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"currentColor\"/>\r\n <path d=\"M12 2V4M12 20V22M4 12H2M22 12H20M19.07 4.93L17.66 6.34M6.34 17.66L4.93 19.07M19.07 19.07L17.66 17.66M6.34 6.34L4.93 4.93\" \r\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n </svg>\r\n </span>\r\n \r\n <!-- Moon Icon (Dark Mode) -->\r\n <span class=\"cide-theme-toggle-icon moon-icon\" [class.hidden]=\"!isDarkMode()\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" \r\n stroke=\"currentColor\" stroke-width=\"2\" fill=\"currentColor\"/>\r\n </svg>\r\n </span>\r\n </span>\r\n \r\n <!-- Toggle Thumb (Slider) -->\r\n <span class=\"cide-theme-toggle-thumb\" [style.transform]=\"togglePosition()\">\r\n </span>\r\n </button>\r\n</div>\r\n\r\n", styles: [".cide-theme-toggle-wrapper{display:inline-flex;align-items:center;justify-content:center}.cide-theme-toggle-button{position:relative;width:52px;height:32px;padding:0;border:none;background:transparent;cursor:pointer;outline:none;transition:all .3s cubic-bezier(.4,0,.2,1);user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.cide-theme-toggle-button:focus-visible{outline:2px solid rgba(59,130,246,.5);outline-offset:2px;border-radius:20px}.cide-theme-toggle-track{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:16px;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;justify-content:space-between;padding:0 4px;box-shadow:inset 0 2px 4px #0000001a,0 1px 2px #0000000d}.is-dark .cide-theme-toggle-track{box-shadow:inset 0 2px 4px #0003,0 1px 2px #0000001a}.cide-theme-toggle-icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;color:#fff;transition:all .3s cubic-bezier(.4,0,.2,1);opacity:1}.cide-theme-toggle-icon.hidden{opacity:0;width:0;transform:scale(0)}.cide-theme-toggle-icon svg{width:16px;height:16px;filter:drop-shadow(0 1px 2px rgba(0,0,0,.2))}.cide-theme-toggle-icon.sun-icon{color:#fbbf24}.cide-theme-toggle-icon.moon-icon{color:#e0e7ff}.cide-theme-toggle-thumb{position:absolute;top:2px;left:2px;width:28px;height:28px;background:#fff;border-radius:50%;box-shadow:0 2px 4px #0003,0 1px 2px #0000001a;transition:transform .3s cubic-bezier(.4,0,.2,1);z-index:1;background:linear-gradient(180deg,#fff,#f9fafb)}.is-dark .cide-theme-toggle-thumb{background:linear-gradient(180deg,#f3f4f6,#e5e7eb);box-shadow:0 2px 4px #0000004d,0 1px 2px #00000026}.is-animating .cide-theme-toggle-thumb{transition:transform .3s cubic-bezier(.4,0,.2,1)}.cide-theme-toggle-button:hover .cide-theme-toggle-thumb{box-shadow:0 3px 6px #00000040,0 2px 4px #00000026}.is-dark .cide-theme-toggle-button:hover .cide-theme-toggle-thumb{box-shadow:0 3px 6px #0006,0 2px 4px #0003}.cide-theme-toggle-button:hover .cide-theme-toggle-track{box-shadow:inset 0 2px 4px #00000026,0 2px 4px #0000001a}.is-dark .cide-theme-toggle-button:hover .cide-theme-toggle-track{box-shadow:inset 0 2px 4px #00000040,0 2px 4px #00000026}.cide-theme-toggle-button:active .cide-theme-toggle-thumb{transform:scale(.95)}.cide-theme-toggle-button:disabled{opacity:.5;cursor:not-allowed}.cide-theme-toggle-button:disabled:hover .cide-theme-toggle-thumb,.cide-theme-toggle-button:disabled:hover .cide-theme-toggle-track{box-shadow:none}@media (prefers-color-scheme: dark){.cide-theme-toggle-thumb{background:linear-gradient(180deg,#f3f4f6,#e5e7eb)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
7722
|
+
}
|
|
7723
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleThemeToggleComponent, decorators: [{
|
|
7724
|
+
type: Component,
|
|
7725
|
+
args: [{ selector: 'cide-ele-theme-toggle', standalone: true, imports: [CommonModule], template: "<div class=\"cide-theme-toggle-wrapper\">\r\n <button\r\n type=\"button\"\r\n class=\"cide-theme-toggle-button\"\r\n [class.is-dark]=\"isDarkMode()\"\r\n [class.is-animating]=\"isAnimating()\"\r\n (click)=\"toggleTheme()\"\r\n [attr.aria-label]=\"isDarkMode() ? 'Switch to light mode' : 'Switch to dark mode'\"\r\n [attr.aria-pressed]=\"isDarkMode()\">\r\n \r\n <!-- Toggle Track -->\r\n <span class=\"cide-theme-toggle-track\" [class]=\"toggleBgColor()\">\r\n <!-- Sun Icon (Light Mode) -->\r\n <span class=\"cide-theme-toggle-icon sun-icon\" [class.hidden]=\"isDarkMode()\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <circle cx=\"12\" cy=\"12\" r=\"4\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"currentColor\"/>\r\n <path d=\"M12 2V4M12 20V22M4 12H2M22 12H20M19.07 4.93L17.66 6.34M6.34 17.66L4.93 19.07M19.07 19.07L17.66 17.66M6.34 6.34L4.93 4.93\" \r\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n </svg>\r\n </span>\r\n \r\n <!-- Moon Icon (Dark Mode) -->\r\n <span class=\"cide-theme-toggle-icon moon-icon\" [class.hidden]=\"!isDarkMode()\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" \r\n stroke=\"currentColor\" stroke-width=\"2\" fill=\"currentColor\"/>\r\n </svg>\r\n </span>\r\n </span>\r\n \r\n <!-- Toggle Thumb (Slider) -->\r\n <span class=\"cide-theme-toggle-thumb\" [style.transform]=\"togglePosition()\">\r\n </span>\r\n </button>\r\n</div>\r\n\r\n", styles: [".cide-theme-toggle-wrapper{display:inline-flex;align-items:center;justify-content:center}.cide-theme-toggle-button{position:relative;width:52px;height:32px;padding:0;border:none;background:transparent;cursor:pointer;outline:none;transition:all .3s cubic-bezier(.4,0,.2,1);user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.cide-theme-toggle-button:focus-visible{outline:2px solid rgba(59,130,246,.5);outline-offset:2px;border-radius:20px}.cide-theme-toggle-track{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:16px;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;justify-content:space-between;padding:0 4px;box-shadow:inset 0 2px 4px #0000001a,0 1px 2px #0000000d}.is-dark .cide-theme-toggle-track{box-shadow:inset 0 2px 4px #0003,0 1px 2px #0000001a}.cide-theme-toggle-icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;color:#fff;transition:all .3s cubic-bezier(.4,0,.2,1);opacity:1}.cide-theme-toggle-icon.hidden{opacity:0;width:0;transform:scale(0)}.cide-theme-toggle-icon svg{width:16px;height:16px;filter:drop-shadow(0 1px 2px rgba(0,0,0,.2))}.cide-theme-toggle-icon.sun-icon{color:#fbbf24}.cide-theme-toggle-icon.moon-icon{color:#e0e7ff}.cide-theme-toggle-thumb{position:absolute;top:2px;left:2px;width:28px;height:28px;background:#fff;border-radius:50%;box-shadow:0 2px 4px #0003,0 1px 2px #0000001a;transition:transform .3s cubic-bezier(.4,0,.2,1);z-index:1;background:linear-gradient(180deg,#fff,#f9fafb)}.is-dark .cide-theme-toggle-thumb{background:linear-gradient(180deg,#f3f4f6,#e5e7eb);box-shadow:0 2px 4px #0000004d,0 1px 2px #00000026}.is-animating .cide-theme-toggle-thumb{transition:transform .3s cubic-bezier(.4,0,.2,1)}.cide-theme-toggle-button:hover .cide-theme-toggle-thumb{box-shadow:0 3px 6px #00000040,0 2px 4px #00000026}.is-dark .cide-theme-toggle-button:hover .cide-theme-toggle-thumb{box-shadow:0 3px 6px #0006,0 2px 4px #0003}.cide-theme-toggle-button:hover .cide-theme-toggle-track{box-shadow:inset 0 2px 4px #00000026,0 2px 4px #0000001a}.is-dark .cide-theme-toggle-button:hover .cide-theme-toggle-track{box-shadow:inset 0 2px 4px #00000040,0 2px 4px #00000026}.cide-theme-toggle-button:active .cide-theme-toggle-thumb{transform:scale(.95)}.cide-theme-toggle-button:disabled{opacity:.5;cursor:not-allowed}.cide-theme-toggle-button:disabled:hover .cide-theme-toggle-thumb,.cide-theme-toggle-button:disabled:hover .cide-theme-toggle-track{box-shadow:none}@media (prefers-color-scheme: dark){.cide-theme-toggle-thumb{background:linear-gradient(180deg,#f3f4f6,#e5e7eb)}}\n"] }]
|
|
7726
|
+
}] });
|
|
7727
|
+
|
|
7728
|
+
/**
|
|
7729
|
+
* Notification API Service
|
|
7730
|
+
* HTTP client for notification API endpoints
|
|
7731
|
+
*/
|
|
7732
|
+
class NotificationApiService {
|
|
7733
|
+
http = inject(HttpClient);
|
|
7734
|
+
apiUrl = cidePath.join([
|
|
7735
|
+
hostManagerRoutesUrl.cideSuiteHost,
|
|
7736
|
+
notificationRoutesUrl.module
|
|
7737
|
+
]);
|
|
7713
7738
|
/**
|
|
7714
|
-
* Get
|
|
7739
|
+
* Get user notifications
|
|
7715
7740
|
*/
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
const req = new HttpRequest('POST', url, formData, {
|
|
7731
|
-
reportProgress: true
|
|
7732
|
-
});
|
|
7733
|
-
return this.http.request(req).pipe(catchError$1(this.handleError));
|
|
7734
|
-
}
|
|
7735
|
-
/**
|
|
7736
|
-
* Upload file with progress tracking (mock version - deprecated)
|
|
7737
|
-
*/
|
|
7738
|
-
uploadFileWithMockData(request) {
|
|
7739
|
-
console.warn('uploadFileWithMockData is deprecated. Use uploadFile instead.');
|
|
7740
|
-
return this.uploadFile(request);
|
|
7741
|
-
}
|
|
7742
|
-
/**
|
|
7743
|
-
* Update file metadata
|
|
7744
|
-
*/
|
|
7745
|
-
updateFile(request) {
|
|
7746
|
-
console.log('Updating file:', request);
|
|
7747
|
-
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager]);
|
|
7748
|
-
return this.http.post(url, request)
|
|
7749
|
-
.pipe(tap((response) => {
|
|
7750
|
-
if (response.success) {
|
|
7751
|
-
this.refreshFileList();
|
|
7752
|
-
}
|
|
7753
|
-
}), catchError$1(this.handleError));
|
|
7754
|
-
}
|
|
7755
|
-
/**
|
|
7756
|
-
* Delete file
|
|
7757
|
-
*/
|
|
7758
|
-
deleteFile(id) {
|
|
7759
|
-
const payload = { cyfm_id: id };
|
|
7760
|
-
const query = generateStringFromObject(payload);
|
|
7761
|
-
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
|
|
7762
|
-
return this.http.delete(url)
|
|
7763
|
-
.pipe(tap((response) => {
|
|
7764
|
-
if (response.success) {
|
|
7765
|
-
this.refreshFileList();
|
|
7766
|
-
}
|
|
7767
|
-
}), catchError$1(this.handleError));
|
|
7768
|
-
}
|
|
7769
|
-
/**
|
|
7770
|
-
* Delete multiple files
|
|
7771
|
-
*/
|
|
7772
|
-
deleteMultipleFiles(ids) {
|
|
7773
|
-
console.log('Deleting multiple files:', ids);
|
|
7774
|
-
const payload = { ids };
|
|
7775
|
-
const query = generateStringFromObject(payload);
|
|
7776
|
-
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
|
|
7777
|
-
return this.http.delete(url)
|
|
7778
|
-
.pipe(tap((response) => {
|
|
7779
|
-
if (response.success) {
|
|
7780
|
-
this.refreshFileList();
|
|
7781
|
-
}
|
|
7782
|
-
}), catchError$1(this.handleError));
|
|
7783
|
-
}
|
|
7784
|
-
/**
|
|
7785
|
-
* Toggle file active status
|
|
7786
|
-
*/
|
|
7787
|
-
toggleFileStatus(id) {
|
|
7788
|
-
console.log('Toggling file status:', id);
|
|
7789
|
-
const payload = { id };
|
|
7790
|
-
const query = generateStringFromObject(payload);
|
|
7791
|
-
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
|
|
7792
|
-
return this.http.put(url, {})
|
|
7793
|
-
.pipe(tap((response) => {
|
|
7794
|
-
if (response.success) {
|
|
7795
|
-
this.refreshFileList();
|
|
7796
|
-
}
|
|
7797
|
-
}), catchError$1(this.handleError));
|
|
7741
|
+
getNotifications(params) {
|
|
7742
|
+
let httpParams = new HttpParams();
|
|
7743
|
+
if (params) {
|
|
7744
|
+
Object.keys(params).forEach(key => {
|
|
7745
|
+
const value = params[key];
|
|
7746
|
+
if (value !== undefined && value !== null) {
|
|
7747
|
+
httpParams = httpParams.set(key, value.toString());
|
|
7748
|
+
}
|
|
7749
|
+
});
|
|
7750
|
+
}
|
|
7751
|
+
// Debug: Log the URL being used
|
|
7752
|
+
console.log('[NotificationApi] Request URL:', this.apiUrl);
|
|
7753
|
+
console.log('[NotificationApi] Full URL with params:', `${this.apiUrl}?${httpParams.toString()}`);
|
|
7754
|
+
return this.http.get(this.apiUrl, { params: httpParams });
|
|
7798
7755
|
}
|
|
7799
7756
|
/**
|
|
7800
|
-
* Get
|
|
7757
|
+
* Get notification by ID
|
|
7801
7758
|
*/
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
const payload = { cyfm_id: id };
|
|
7805
|
-
const query = generateStringFromObject(payload);
|
|
7806
|
-
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, 'byId', query]);
|
|
7807
|
-
return this.http.get(url)
|
|
7808
|
-
.pipe(catchError$1(this.handleError));
|
|
7759
|
+
getNotificationById(id) {
|
|
7760
|
+
return this.http.get(`${this.apiUrl}/${id}`);
|
|
7809
7761
|
}
|
|
7810
7762
|
/**
|
|
7811
|
-
*
|
|
7763
|
+
* Get unread count
|
|
7812
7764
|
*/
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
if (item._id === id) {
|
|
7816
|
-
return item;
|
|
7817
|
-
}
|
|
7818
|
-
}
|
|
7819
|
-
return null;
|
|
7765
|
+
getUnreadCount() {
|
|
7766
|
+
return this.http.get(`${this.apiUrl}/${notificationRoutesUrl.unreadCount}`);
|
|
7820
7767
|
}
|
|
7821
7768
|
/**
|
|
7822
|
-
*
|
|
7769
|
+
* Create notification
|
|
7823
7770
|
*/
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
return '0 Bytes';
|
|
7827
|
-
const k = 1024;
|
|
7828
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
7829
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
7830
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
7771
|
+
createNotification(payload) {
|
|
7772
|
+
return this.http.post(this.apiUrl, payload);
|
|
7831
7773
|
}
|
|
7832
7774
|
/**
|
|
7833
|
-
*
|
|
7775
|
+
* Mark notification as read
|
|
7834
7776
|
*/
|
|
7835
|
-
|
|
7836
|
-
const
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
'image/gif': 'image',
|
|
7840
|
-
'image/svg+xml': 'image',
|
|
7841
|
-
'application/pdf': 'picture_as_pdf',
|
|
7842
|
-
'text/plain': 'description',
|
|
7843
|
-
'text/html': 'code',
|
|
7844
|
-
'application/json': 'code',
|
|
7845
|
-
'application/javascript': 'code',
|
|
7846
|
-
'text/css': 'code',
|
|
7847
|
-
'application/zip': 'folder_zip',
|
|
7848
|
-
'application/x-zip-compressed': 'folder_zip',
|
|
7849
|
-
'application/msword': 'description',
|
|
7850
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'description',
|
|
7851
|
-
'application/vnd.ms-excel': 'table_chart',
|
|
7852
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'table_chart',
|
|
7853
|
-
'application/vnd.ms-powerpoint': 'slideshow',
|
|
7854
|
-
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'slideshow'
|
|
7855
|
-
};
|
|
7856
|
-
return typeMap[fileType] || 'insert_drive_file';
|
|
7777
|
+
markAsRead(id) {
|
|
7778
|
+
const url = `${this.apiUrl}/${id}/read`;
|
|
7779
|
+
console.log('[NotificationApi] Mark as read URL:', url);
|
|
7780
|
+
return this.http.put(url, {});
|
|
7857
7781
|
}
|
|
7858
7782
|
/**
|
|
7859
|
-
*
|
|
7783
|
+
* Mark all notifications as read
|
|
7860
7784
|
*/
|
|
7861
|
-
|
|
7862
|
-
const
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
};
|
|
7866
|
-
this.getFileList(defaultBody).subscribe({
|
|
7867
|
-
next: () => {
|
|
7868
|
-
console.log('File list refreshed successfully');
|
|
7869
|
-
},
|
|
7870
|
-
error: (error) => {
|
|
7871
|
-
console.error('Error refreshing file list:', error);
|
|
7872
|
-
}
|
|
7873
|
-
});
|
|
7785
|
+
markAllAsRead() {
|
|
7786
|
+
const url = `${this.apiUrl}/read-all`;
|
|
7787
|
+
console.log('[NotificationApi] Mark all as read URL:', url);
|
|
7788
|
+
return this.http.put(url, {});
|
|
7874
7789
|
}
|
|
7875
7790
|
/**
|
|
7876
|
-
*
|
|
7791
|
+
* Archive notification
|
|
7877
7792
|
*/
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
if (error instanceof Error) {
|
|
7881
|
-
errorMessage = error.message;
|
|
7882
|
-
}
|
|
7883
|
-
else if (typeof error === 'string') {
|
|
7884
|
-
errorMessage = error;
|
|
7885
|
-
}
|
|
7886
|
-
console.error('CideCoreFileManagerService Error:', errorMessage);
|
|
7887
|
-
return throwError(() => new Error(errorMessage));
|
|
7793
|
+
archiveNotification(id) {
|
|
7794
|
+
return this.http.put(`${this.apiUrl}/${id}/archive`, {});
|
|
7888
7795
|
}
|
|
7889
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
7890
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
7796
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NotificationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7797
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NotificationApiService, providedIn: 'root' });
|
|
7891
7798
|
}
|
|
7892
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
7799
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NotificationApiService, decorators: [{
|
|
7893
7800
|
type: Injectable,
|
|
7894
7801
|
args: [{
|
|
7895
7802
|
providedIn: 'root'
|
|
7896
7803
|
}]
|
|
7897
|
-
}]
|
|
7804
|
+
}] });
|
|
7898
7805
|
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
this.
|
|
7924
|
-
|
|
7925
|
-
const newMap = new Map(map);
|
|
7926
|
-
newMap.set(dropdown.id, dropdown);
|
|
7927
|
-
return newMap;
|
|
7928
|
-
});
|
|
7929
|
-
}
|
|
7930
|
-
unregisterDropdown(id) {
|
|
7931
|
-
this.activeDropdowns.update(map => {
|
|
7932
|
-
const newMap = new Map(map);
|
|
7933
|
-
newMap.delete(id);
|
|
7934
|
-
return newMap;
|
|
7935
|
-
});
|
|
7936
|
-
}
|
|
7937
|
-
closeAllDropdowns() {
|
|
7938
|
-
const dropdowns = this.activeDropdowns();
|
|
7939
|
-
dropdowns.forEach(dropdown => {
|
|
7940
|
-
dropdown.close();
|
|
7941
|
-
});
|
|
7942
|
-
this.activeDropdowns.set(new Map());
|
|
7943
|
-
}
|
|
7944
|
-
closeDropdown(id) {
|
|
7945
|
-
const dropdowns = this.activeDropdowns();
|
|
7946
|
-
const dropdown = dropdowns.get(id);
|
|
7947
|
-
if (dropdown) {
|
|
7948
|
-
dropdown.close();
|
|
7949
|
-
this.unregisterDropdown(id);
|
|
7950
|
-
}
|
|
7951
|
-
}
|
|
7952
|
-
isDropdownOpen(id) {
|
|
7953
|
-
return this.activeDropdowns().has(id);
|
|
7954
|
-
}
|
|
7955
|
-
handleDocumentClick(event) {
|
|
7956
|
-
const dropdowns = this.activeDropdowns();
|
|
7957
|
-
if (dropdowns.size === 0) {
|
|
7806
|
+
/**
|
|
7807
|
+
* WebSocket Notification Service
|
|
7808
|
+
* Socket.IO client for real-time notifications
|
|
7809
|
+
*/
|
|
7810
|
+
class WebSocketNotificationService {
|
|
7811
|
+
socket = null;
|
|
7812
|
+
notificationSubject = new Subject();
|
|
7813
|
+
connectionStatusSubject = new BehaviorSubject({
|
|
7814
|
+
connected: false
|
|
7815
|
+
});
|
|
7816
|
+
// Signals for reactive state
|
|
7817
|
+
notifications = signal([], ...(ngDevMode ? [{ debugName: "notifications" }] : []));
|
|
7818
|
+
unreadNotifications = signal([], ...(ngDevMode ? [{ debugName: "unreadNotifications" }] : []));
|
|
7819
|
+
// Public observables
|
|
7820
|
+
notification$ = this.notificationSubject.asObservable();
|
|
7821
|
+
connectionStatus$ = this.connectionStatusSubject.asObservable();
|
|
7822
|
+
// Computed signals
|
|
7823
|
+
allNotifications = computed(() => this.notifications(), ...(ngDevMode ? [{ debugName: "allNotifications" }] : []));
|
|
7824
|
+
unreadNotificationsCount = computed(() => this.unreadNotifications().length, ...(ngDevMode ? [{ debugName: "unreadNotificationsCount" }] : []));
|
|
7825
|
+
isConnected = computed(() => this.connectionStatusSubject.value.connected, ...(ngDevMode ? [{ debugName: "isConnected" }] : []));
|
|
7826
|
+
/**
|
|
7827
|
+
* Connect to Socket.IO server
|
|
7828
|
+
*/
|
|
7829
|
+
connect(token, userId) {
|
|
7830
|
+
if (this.socket?.connected) {
|
|
7831
|
+
console.log('Socket already connected');
|
|
7958
7832
|
return;
|
|
7959
7833
|
}
|
|
7960
|
-
//
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7834
|
+
// Resolve actual API URL from placeholder
|
|
7835
|
+
// The placeholder is replaced by host interceptor for HTTP, but WebSocket needs direct resolution
|
|
7836
|
+
// Based on host-manager.json: "__cloud_ide_suite_layout__" -> "http://localhost:3001"
|
|
7837
|
+
const placeholder = hostManagerRoutesUrl.cideSuiteHost;
|
|
7838
|
+
let apiUrl = 'http://localhost:3001'; // Default from host-manager.json
|
|
7839
|
+
// Try to resolve from host-manager.json via fetch (same as HTTP interceptor)
|
|
7840
|
+
if (typeof window !== 'undefined') {
|
|
7841
|
+
// Try to fetch host-manager.json to get the actual URL
|
|
7842
|
+
fetch('/routes/host-manager.json')
|
|
7843
|
+
.then(res => res.json())
|
|
7844
|
+
.then((hostManagerRoutes) => {
|
|
7845
|
+
const hostManagerRoutesEntry = hostManagerRoutes?.hosts?.find((hostRoute) => placeholder?.includes(hostRoute?.url));
|
|
7846
|
+
if (hostManagerRoutesEntry?.replace) {
|
|
7847
|
+
apiUrl = hostManagerRoutesEntry.replace;
|
|
7848
|
+
}
|
|
7849
|
+
// Remove /api suffix if present (Socket.IO connects to base server)
|
|
7850
|
+
apiUrl = apiUrl.replace(/\/api\/?$/, '');
|
|
7851
|
+
this.connectWithUrl(apiUrl, token, userId);
|
|
7852
|
+
})
|
|
7853
|
+
.catch(() => {
|
|
7854
|
+
// Fallback if fetch fails - use default or try localStorage
|
|
7855
|
+
this.resolveUrlFallback(token, userId);
|
|
7856
|
+
});
|
|
7857
|
+
}
|
|
7858
|
+
else {
|
|
7859
|
+
// Server-side or fallback
|
|
7860
|
+
this.resolveUrlFallback(token, userId);
|
|
7971
7861
|
}
|
|
7972
7862
|
}
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7863
|
+
/**
|
|
7864
|
+
* Fallback URL resolution
|
|
7865
|
+
*/
|
|
7866
|
+
resolveUrlFallback(token, userId) {
|
|
7867
|
+
let apiUrl = 'http://localhost:3001'; // Default from host-manager.json
|
|
7868
|
+
// Try to get from current window location
|
|
7869
|
+
if (typeof window !== 'undefined') {
|
|
7870
|
+
const currentOrigin = window.location.origin;
|
|
7871
|
+
// Check if we're on localhost (development)
|
|
7872
|
+
if (currentOrigin.includes('localhost') || currentOrigin.includes('127.0.0.1')) {
|
|
7873
|
+
// Use localhost:3001 for development (from host-manager.json)
|
|
7874
|
+
apiUrl = 'http://localhost:3001';
|
|
7875
|
+
}
|
|
7876
|
+
else {
|
|
7877
|
+
// In production, use the same origin (assuming API is on same domain)
|
|
7878
|
+
apiUrl = currentOrigin || 'http://localhost:3001';
|
|
7879
|
+
}
|
|
7880
|
+
// Try to get from localStorage (if set by app initialization)
|
|
7881
|
+
const storedApiUrl = localStorage.getItem('api_base_url');
|
|
7882
|
+
if (storedApiUrl) {
|
|
7883
|
+
apiUrl = storedApiUrl.replace(/\/api\/?$/, '');
|
|
7884
|
+
}
|
|
7980
7885
|
}
|
|
7886
|
+
this.connectWithUrl(apiUrl, token, userId);
|
|
7981
7887
|
}
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7888
|
+
/**
|
|
7889
|
+
* Connect with resolved URL
|
|
7890
|
+
*/
|
|
7891
|
+
connectWithUrl(apiUrl, token, userId) {
|
|
7892
|
+
console.log('[WebSocket] Connecting to:', apiUrl);
|
|
7893
|
+
this.socket = io(apiUrl, {
|
|
7894
|
+
path: '/notifications-socket',
|
|
7895
|
+
transports: ['websocket', 'polling'],
|
|
7896
|
+
auth: {
|
|
7897
|
+
token: token
|
|
7898
|
+
},
|
|
7899
|
+
reconnection: true,
|
|
7900
|
+
reconnectionDelay: 1000,
|
|
7901
|
+
reconnectionAttempts: 5,
|
|
7902
|
+
reconnectionDelayMax: 5000
|
|
7903
|
+
});
|
|
7904
|
+
this.setupEventHandlers(userId);
|
|
7998
7905
|
}
|
|
7999
|
-
|
|
8000
|
-
|
|
7906
|
+
/**
|
|
7907
|
+
* Setup Socket.IO event handlers
|
|
7908
|
+
*/
|
|
7909
|
+
setupEventHandlers(userId) {
|
|
7910
|
+
if (!this.socket)
|
|
7911
|
+
return;
|
|
7912
|
+
// Connection established
|
|
7913
|
+
this.socket.on('connect', () => {
|
|
7914
|
+
console.log('Socket.IO connected:', this.socket?.id);
|
|
7915
|
+
this.connectionStatusSubject.next({
|
|
7916
|
+
connected: true,
|
|
7917
|
+
socketId: this.socket?.id,
|
|
7918
|
+
userId: userId,
|
|
7919
|
+
timestamp: new Date()
|
|
7920
|
+
});
|
|
7921
|
+
});
|
|
7922
|
+
// Connection confirmation
|
|
7923
|
+
this.socket.on('connected', (data) => {
|
|
7924
|
+
console.log('Notification service connected:', data);
|
|
7925
|
+
this.connectionStatusSubject.next({
|
|
7926
|
+
connected: true,
|
|
7927
|
+
socketId: data.socketId,
|
|
7928
|
+
userId: data.userId,
|
|
7929
|
+
timestamp: data.timestamp
|
|
7930
|
+
});
|
|
7931
|
+
});
|
|
7932
|
+
// Receive notification
|
|
7933
|
+
this.socket.on('notification', (notification) => {
|
|
7934
|
+
console.log('Received notification:', notification);
|
|
7935
|
+
// Add to notifications list
|
|
7936
|
+
this.notifications.update(notifications => [notification, ...notifications]);
|
|
7937
|
+
// Add to unread list
|
|
7938
|
+
this.unreadNotifications.update(unread => [notification, ...unread]);
|
|
7939
|
+
// Emit to subscribers
|
|
7940
|
+
this.notificationSubject.next(notification);
|
|
7941
|
+
// Acknowledge receipt
|
|
7942
|
+
this.acknowledgeNotification(notification.id);
|
|
7943
|
+
});
|
|
7944
|
+
// Disconnect
|
|
7945
|
+
this.socket.on('disconnect', (reason) => {
|
|
7946
|
+
console.log('Socket.IO disconnected:', reason);
|
|
7947
|
+
this.connectionStatusSubject.next({
|
|
7948
|
+
connected: false,
|
|
7949
|
+
timestamp: new Date()
|
|
7950
|
+
});
|
|
7951
|
+
});
|
|
7952
|
+
// Connection error
|
|
7953
|
+
this.socket.on('connect_error', (error) => {
|
|
7954
|
+
console.error('Socket.IO connection error:', error);
|
|
7955
|
+
this.connectionStatusSubject.next({
|
|
7956
|
+
connected: false,
|
|
7957
|
+
timestamp: new Date()
|
|
7958
|
+
});
|
|
7959
|
+
});
|
|
8001
7960
|
}
|
|
8002
7961
|
/**
|
|
8003
|
-
*
|
|
7962
|
+
* Acknowledge notification receipt
|
|
8004
7963
|
*/
|
|
8005
|
-
|
|
8006
|
-
this.
|
|
8007
|
-
|
|
7964
|
+
acknowledgeNotification(notificationId) {
|
|
7965
|
+
if (this.socket?.connected) {
|
|
7966
|
+
this.socket.emit('notification:ack', { notificationId });
|
|
7967
|
+
}
|
|
8008
7968
|
}
|
|
8009
7969
|
/**
|
|
8010
|
-
*
|
|
7970
|
+
* Mark notification as read
|
|
8011
7971
|
*/
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
7972
|
+
markAsRead(notificationId) {
|
|
7973
|
+
if (this.socket?.connected) {
|
|
7974
|
+
this.socket.emit('notification:read', { notificationId });
|
|
7975
|
+
// Remove from unread list
|
|
7976
|
+
this.unreadNotifications.update(unread => unread.filter(n => n.id !== notificationId));
|
|
8017
7977
|
}
|
|
8018
|
-
const override = {
|
|
8019
|
-
shortcutId,
|
|
8020
|
-
newKey,
|
|
8021
|
-
newCtrlKey: options?.ctrlKey,
|
|
8022
|
-
newAltKey: options?.altKey,
|
|
8023
|
-
newShiftKey: options?.shiftKey,
|
|
8024
|
-
newMetaKey: options?.metaKey
|
|
8025
|
-
};
|
|
8026
|
-
this.overrides.set(shortcutId, override);
|
|
8027
|
-
console.log(`🔄 [KeyboardShortcut] Override registered for '${shortcutId}': ${this.getKeyDescription({ ...originalShortcut, ...options, key: newKey })}`);
|
|
8028
7978
|
}
|
|
8029
7979
|
/**
|
|
8030
|
-
*
|
|
7980
|
+
* Mark all as read
|
|
8031
7981
|
*/
|
|
8032
|
-
|
|
8033
|
-
this.
|
|
8034
|
-
this.
|
|
8035
|
-
console.log(`🗑️ [KeyboardShortcut] Removed shortcut: ${shortcutId}`);
|
|
7982
|
+
markAllAsRead() {
|
|
7983
|
+
const unreadIds = this.unreadNotifications().map(n => n.id);
|
|
7984
|
+
unreadIds.forEach(id => this.markAsRead(id));
|
|
8036
7985
|
}
|
|
8037
7986
|
/**
|
|
8038
|
-
*
|
|
7987
|
+
* Disconnect from server
|
|
8039
7988
|
*/
|
|
8040
|
-
|
|
8041
|
-
this.
|
|
8042
|
-
|
|
7989
|
+
disconnect() {
|
|
7990
|
+
if (this.socket) {
|
|
7991
|
+
this.socket.disconnect();
|
|
7992
|
+
this.socket = null;
|
|
7993
|
+
this.connectionStatusSubject.next({
|
|
7994
|
+
connected: false,
|
|
7995
|
+
timestamp: new Date()
|
|
7996
|
+
});
|
|
7997
|
+
}
|
|
8043
7998
|
}
|
|
8044
7999
|
/**
|
|
8045
|
-
*
|
|
8000
|
+
* Clear all notifications
|
|
8046
8001
|
*/
|
|
8047
|
-
|
|
8048
|
-
|
|
8002
|
+
clearNotifications() {
|
|
8003
|
+
this.notifications.set([]);
|
|
8004
|
+
this.unreadNotifications.set([]);
|
|
8049
8005
|
}
|
|
8050
8006
|
/**
|
|
8051
|
-
*
|
|
8007
|
+
* Remove notification from list
|
|
8052
8008
|
*/
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8009
|
+
removeNotification(notificationId) {
|
|
8010
|
+
this.notifications.update(notifications => notifications.filter(n => n.id !== notificationId));
|
|
8011
|
+
this.unreadNotifications.update(unread => unread.filter(n => n.id !== notificationId));
|
|
8012
|
+
}
|
|
8013
|
+
ngOnDestroy() {
|
|
8014
|
+
this.disconnect();
|
|
8015
|
+
this.notificationSubject.complete();
|
|
8016
|
+
this.connectionStatusSubject.complete();
|
|
8017
|
+
}
|
|
8018
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: WebSocketNotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8019
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: WebSocketNotificationService, providedIn: 'root' });
|
|
8020
|
+
}
|
|
8021
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: WebSocketNotificationService, decorators: [{
|
|
8022
|
+
type: Injectable,
|
|
8023
|
+
args: [{
|
|
8024
|
+
providedIn: 'root'
|
|
8025
|
+
}]
|
|
8026
|
+
}] });
|
|
8027
|
+
|
|
8028
|
+
class CideCoreFileManagerService {
|
|
8029
|
+
http;
|
|
8030
|
+
apiUrl = `${coreRoutesUrl?.fileManager}`;
|
|
8031
|
+
fileListSubject = new BehaviorSubject([]);
|
|
8032
|
+
fileList$ = this.fileListSubject.asObservable();
|
|
8033
|
+
constructor(http) {
|
|
8034
|
+
this.http = http;
|
|
8035
|
+
console.log('CideCoreFileManagerService initialized - using real API');
|
|
8056
8036
|
}
|
|
8057
8037
|
/**
|
|
8058
|
-
*
|
|
8038
|
+
* Get file list from API
|
|
8059
8039
|
*/
|
|
8060
|
-
|
|
8061
|
-
|
|
8040
|
+
getFileList(body) {
|
|
8041
|
+
const query = generateStringFromObject(body);
|
|
8042
|
+
return this.http?.get(cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]))
|
|
8043
|
+
.pipe(tap((response) => {
|
|
8044
|
+
if (response?.success) {
|
|
8045
|
+
this.fileListSubject.next(response?.data || []);
|
|
8046
|
+
}
|
|
8047
|
+
}), catchError$1(error => {
|
|
8048
|
+
console.error('CideCoreFileManagerService API error:', error);
|
|
8049
|
+
return this.handleError(error);
|
|
8050
|
+
}));
|
|
8062
8051
|
}
|
|
8063
8052
|
/**
|
|
8064
|
-
* Get
|
|
8053
|
+
* Get file list using mock data (deprecated - use getFileList instead)
|
|
8065
8054
|
*/
|
|
8066
|
-
|
|
8067
|
-
|
|
8055
|
+
getFileListWithMockData(_body) {
|
|
8056
|
+
console.warn('getFileListWithMockData is deprecated. Use getFileList instead.');
|
|
8057
|
+
return this.getFileList(_body);
|
|
8068
8058
|
}
|
|
8069
8059
|
/**
|
|
8070
|
-
*
|
|
8060
|
+
* Get file list from cache (if available)
|
|
8071
8061
|
*/
|
|
8072
|
-
|
|
8073
|
-
this.
|
|
8074
|
-
this.overrides.clear();
|
|
8075
|
-
console.log(`🧹 [KeyboardShortcut] All shortcuts cleared`);
|
|
8062
|
+
getFileListFromCache() {
|
|
8063
|
+
return this.fileListSubject.value;
|
|
8076
8064
|
}
|
|
8077
8065
|
/**
|
|
8078
|
-
*
|
|
8066
|
+
* Upload file with progress tracking
|
|
8079
8067
|
*/
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8068
|
+
uploadFile(request) {
|
|
8069
|
+
const formData = new FormData();
|
|
8070
|
+
formData.append('file', request.file);
|
|
8071
|
+
formData.append('altText', request.altText || '');
|
|
8072
|
+
formData.append('tags', JSON.stringify(request.tags || []));
|
|
8073
|
+
formData.append('permissions', JSON.stringify(request.permissions || []));
|
|
8074
|
+
formData.append('userId', request.userId || '');
|
|
8075
|
+
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, 'upload']);
|
|
8076
|
+
const req = new HttpRequest('POST', url, formData, {
|
|
8077
|
+
reportProgress: true
|
|
8078
|
+
});
|
|
8079
|
+
return this.http.request(req).pipe(catchError$1(this.handleError));
|
|
8086
8080
|
}
|
|
8087
8081
|
/**
|
|
8088
|
-
*
|
|
8082
|
+
* Upload file with progress tracking (mock version - deprecated)
|
|
8089
8083
|
*/
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
this.keydownListener = undefined;
|
|
8094
|
-
console.log(`🎧 [KeyboardShortcut] Global keyboard listener removed`);
|
|
8095
|
-
}
|
|
8084
|
+
uploadFileWithMockData(request) {
|
|
8085
|
+
console.warn('uploadFileWithMockData is deprecated. Use uploadFile instead.');
|
|
8086
|
+
return this.uploadFile(request);
|
|
8096
8087
|
}
|
|
8097
8088
|
/**
|
|
8098
|
-
*
|
|
8089
|
+
* Update file metadata
|
|
8099
8090
|
*/
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
}
|
|
8108
|
-
if (shortcut.preventDefault !== false) {
|
|
8109
|
-
event.preventDefault();
|
|
8110
|
-
}
|
|
8111
|
-
if (shortcut.stopPropagation) {
|
|
8112
|
-
event.stopPropagation();
|
|
8113
|
-
}
|
|
8114
|
-
console.log(`⌨️ [KeyboardShortcut] Executing shortcut: ${shortcutId}`);
|
|
8115
|
-
shortcut.action();
|
|
8116
|
-
break; // Only execute one shortcut per keydown
|
|
8091
|
+
updateFile(request) {
|
|
8092
|
+
console.log('Updating file:', request);
|
|
8093
|
+
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager]);
|
|
8094
|
+
return this.http.post(url, request)
|
|
8095
|
+
.pipe(tap((response) => {
|
|
8096
|
+
if (response.success) {
|
|
8097
|
+
this.refreshFileList();
|
|
8117
8098
|
}
|
|
8118
|
-
}
|
|
8099
|
+
}), catchError$1(this.handleError));
|
|
8119
8100
|
}
|
|
8120
8101
|
/**
|
|
8121
|
-
*
|
|
8102
|
+
* Delete file
|
|
8122
8103
|
*/
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
(
|
|
8104
|
+
deleteFile(id) {
|
|
8105
|
+
const payload = { cyfm_id: id };
|
|
8106
|
+
const query = generateStringFromObject(payload);
|
|
8107
|
+
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
|
|
8108
|
+
return this.http.delete(url)
|
|
8109
|
+
.pipe(tap((response) => {
|
|
8110
|
+
if (response.success) {
|
|
8111
|
+
this.refreshFileList();
|
|
8112
|
+
}
|
|
8113
|
+
}), catchError$1(this.handleError));
|
|
8129
8114
|
}
|
|
8130
8115
|
/**
|
|
8131
|
-
*
|
|
8116
|
+
* Delete multiple files
|
|
8132
8117
|
*/
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8118
|
+
deleteMultipleFiles(ids) {
|
|
8119
|
+
console.log('Deleting multiple files:', ids);
|
|
8120
|
+
const payload = { ids };
|
|
8121
|
+
const query = generateStringFromObject(payload);
|
|
8122
|
+
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
|
|
8123
|
+
return this.http.delete(url)
|
|
8124
|
+
.pipe(tap((response) => {
|
|
8125
|
+
if (response.success) {
|
|
8126
|
+
this.refreshFileList();
|
|
8127
|
+
}
|
|
8128
|
+
}), catchError$1(this.handleError));
|
|
8139
8129
|
}
|
|
8140
8130
|
/**
|
|
8141
|
-
*
|
|
8131
|
+
* Toggle file active status
|
|
8142
8132
|
*/
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
8133
|
+
toggleFileStatus(id) {
|
|
8134
|
+
console.log('Toggling file status:', id);
|
|
8135
|
+
const payload = { id };
|
|
8136
|
+
const query = generateStringFromObject(payload);
|
|
8137
|
+
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
|
|
8138
|
+
return this.http.put(url, {})
|
|
8139
|
+
.pipe(tap((response) => {
|
|
8140
|
+
if (response.success) {
|
|
8141
|
+
this.refreshFileList();
|
|
8142
|
+
}
|
|
8143
|
+
}), catchError$1(this.handleError));
|
|
8154
8144
|
}
|
|
8155
8145
|
/**
|
|
8156
|
-
* Get
|
|
8146
|
+
* Get file by ID
|
|
8157
8147
|
*/
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
const
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
key: override.newKey,
|
|
8166
|
-
ctrlKey: override.newCtrlKey,
|
|
8167
|
-
altKey: override.newAltKey,
|
|
8168
|
-
shiftKey: override.newShiftKey,
|
|
8169
|
-
metaKey: override.newMetaKey
|
|
8170
|
-
});
|
|
8171
|
-
}
|
|
8172
|
-
return this.getKeyDescription(shortcut);
|
|
8173
|
-
}
|
|
8174
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: KeyboardShortcutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8175
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: KeyboardShortcutService, providedIn: 'root' });
|
|
8176
|
-
}
|
|
8177
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: KeyboardShortcutService, decorators: [{
|
|
8178
|
-
type: Injectable,
|
|
8179
|
-
args: [{
|
|
8180
|
-
providedIn: 'root'
|
|
8181
|
-
}]
|
|
8182
|
-
}], ctorParameters: () => [] });
|
|
8183
|
-
|
|
8184
|
-
class ConfirmationService {
|
|
8185
|
-
// Modern signal-based state management
|
|
8186
|
-
confirmationQueue = signal([], ...(ngDevMode ? [{ debugName: "confirmationQueue" }] : []));
|
|
8187
|
-
currentRequest = signal(null, ...(ngDevMode ? [{ debugName: "currentRequest" }] : []));
|
|
8188
|
-
// Modern computed signals with proper typing
|
|
8189
|
-
hasActiveConfirmation = computed(() => this.currentRequest() !== null, ...(ngDevMode ? [{ debugName: "hasActiveConfirmation" }] : []));
|
|
8190
|
-
queueLength = computed(() => this.confirmationQueue().length, ...(ngDevMode ? [{ debugName: "queueLength" }] : []));
|
|
8191
|
-
constructor() {
|
|
8192
|
-
// Modern service initialization
|
|
8193
|
-
}
|
|
8194
|
-
ngOnDestroy() {
|
|
8195
|
-
// Cleanup if needed
|
|
8148
|
+
getFileById(id) {
|
|
8149
|
+
console.log('Getting file by ID:', id);
|
|
8150
|
+
const payload = { cyfm_id: id };
|
|
8151
|
+
const query = generateStringFromObject(payload);
|
|
8152
|
+
const url = cidePath$1?.join([hostManagerRoutesUrl$1?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, 'byId', query]);
|
|
8153
|
+
return this.http.get(url)
|
|
8154
|
+
.pipe(catchError$1(this.handleError));
|
|
8196
8155
|
}
|
|
8197
8156
|
/**
|
|
8198
|
-
*
|
|
8157
|
+
* Find file by ID
|
|
8199
8158
|
*/
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
type: options.type || 'warning',
|
|
8208
|
-
icon: options.icon || this.getDefaultIcon(options.type || 'warning'),
|
|
8209
|
-
confirmText: options.confirmText || 'Confirm',
|
|
8210
|
-
cancelText: options.cancelText || 'Cancel',
|
|
8211
|
-
resolve: (value) => resolve(value),
|
|
8212
|
-
reject
|
|
8213
|
-
};
|
|
8214
|
-
console.log('🔽 Ask for confirmation with a simple yes/no dialog', request);
|
|
8215
|
-
this.addToQueue(request);
|
|
8216
|
-
});
|
|
8159
|
+
findFileById(id, items = this.fileListSubject.value) {
|
|
8160
|
+
for (const item of items) {
|
|
8161
|
+
if (item._id === id) {
|
|
8162
|
+
return item;
|
|
8163
|
+
}
|
|
8164
|
+
}
|
|
8165
|
+
return null;
|
|
8217
8166
|
}
|
|
8218
8167
|
/**
|
|
8219
|
-
*
|
|
8168
|
+
* Get file size in human readable format
|
|
8220
8169
|
*/
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
icon: options.icon || this.getDefaultIcon(options.type || 'info'),
|
|
8229
|
-
confirmText: options.confirmText || 'Confirm',
|
|
8230
|
-
cancelText: options.cancelText || 'Cancel',
|
|
8231
|
-
customTemplate: options.customTemplate,
|
|
8232
|
-
customData: options.customData,
|
|
8233
|
-
resolve: (value) => resolve(value),
|
|
8234
|
-
reject
|
|
8235
|
-
};
|
|
8236
|
-
// Cast to unknown for queue compatibility
|
|
8237
|
-
this.addToQueue(request);
|
|
8238
|
-
});
|
|
8170
|
+
getFileSizeDisplay(bytes) {
|
|
8171
|
+
if (bytes === 0)
|
|
8172
|
+
return '0 Bytes';
|
|
8173
|
+
const k = 1024;
|
|
8174
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
8175
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
8176
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
8239
8177
|
}
|
|
8240
8178
|
/**
|
|
8241
|
-
*
|
|
8179
|
+
* Get file type icon
|
|
8242
8180
|
*/
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8181
|
+
getFileTypeIcon(fileType) {
|
|
8182
|
+
const typeMap = {
|
|
8183
|
+
'image/jpeg': 'image',
|
|
8184
|
+
'image/png': 'image',
|
|
8185
|
+
'image/gif': 'image',
|
|
8186
|
+
'image/svg+xml': 'image',
|
|
8187
|
+
'application/pdf': 'picture_as_pdf',
|
|
8188
|
+
'text/plain': 'description',
|
|
8189
|
+
'text/html': 'code',
|
|
8190
|
+
'application/json': 'code',
|
|
8191
|
+
'application/javascript': 'code',
|
|
8192
|
+
'text/css': 'code',
|
|
8193
|
+
'application/zip': 'folder_zip',
|
|
8194
|
+
'application/x-zip-compressed': 'folder_zip',
|
|
8195
|
+
'application/msword': 'description',
|
|
8196
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'description',
|
|
8197
|
+
'application/vnd.ms-excel': 'table_chart',
|
|
8198
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'table_chart',
|
|
8199
|
+
'application/vnd.ms-powerpoint': 'slideshow',
|
|
8200
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'slideshow'
|
|
8201
|
+
};
|
|
8202
|
+
return typeMap[fileType] || 'insert_drive_file';
|
|
8252
8203
|
}
|
|
8253
8204
|
/**
|
|
8254
|
-
*
|
|
8205
|
+
* Refresh file list from server
|
|
8255
8206
|
*/
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8207
|
+
refreshFileList() {
|
|
8208
|
+
const defaultBody = {
|
|
8209
|
+
pageIndex: 1,
|
|
8210
|
+
pageSize: 10
|
|
8211
|
+
};
|
|
8212
|
+
this.getFileList(defaultBody).subscribe({
|
|
8213
|
+
next: () => {
|
|
8214
|
+
console.log('File list refreshed successfully');
|
|
8215
|
+
},
|
|
8216
|
+
error: (error) => {
|
|
8217
|
+
console.error('Error refreshing file list:', error);
|
|
8218
|
+
}
|
|
8264
8219
|
});
|
|
8265
8220
|
}
|
|
8266
8221
|
/**
|
|
8267
|
-
*
|
|
8222
|
+
* Handle errors
|
|
8268
8223
|
*/
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8224
|
+
handleError(error) {
|
|
8225
|
+
let errorMessage = 'An error occurred';
|
|
8226
|
+
if (error instanceof Error) {
|
|
8227
|
+
errorMessage = error.message;
|
|
8228
|
+
}
|
|
8229
|
+
else if (typeof error === 'string') {
|
|
8230
|
+
errorMessage = error;
|
|
8231
|
+
}
|
|
8232
|
+
console.error('CideCoreFileManagerService Error:', errorMessage);
|
|
8233
|
+
return throwError(() => new Error(errorMessage));
|
|
8278
8234
|
}
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8235
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideCoreFileManagerService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8236
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideCoreFileManagerService, providedIn: 'root' });
|
|
8237
|
+
}
|
|
8238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideCoreFileManagerService, decorators: [{
|
|
8239
|
+
type: Injectable,
|
|
8240
|
+
args: [{
|
|
8241
|
+
providedIn: 'root'
|
|
8242
|
+
}]
|
|
8243
|
+
}], ctorParameters: () => [{ type: i1$1.HttpClient }] });
|
|
8244
|
+
|
|
8245
|
+
class DropdownManagerService {
|
|
8246
|
+
activeDropdowns = signal(new Map(), ...(ngDevMode ? [{ debugName: "activeDropdowns" }] : []));
|
|
8247
|
+
documentClickListener;
|
|
8248
|
+
escapeKeyListener;
|
|
8249
|
+
constructor() {
|
|
8250
|
+
this.initializeGlobalListeners();
|
|
8284
8251
|
}
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
}
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8252
|
+
initializeGlobalListeners() {
|
|
8253
|
+
// Global document click listener
|
|
8254
|
+
this.documentClickListener = (event) => {
|
|
8255
|
+
this.handleDocumentClick(event);
|
|
8256
|
+
};
|
|
8257
|
+
// Global escape key listener
|
|
8258
|
+
this.escapeKeyListener = (event) => {
|
|
8259
|
+
if (event.key === 'Escape') {
|
|
8260
|
+
this.closeAllDropdowns();
|
|
8261
|
+
}
|
|
8262
|
+
};
|
|
8263
|
+
// Add listeners
|
|
8264
|
+
document.addEventListener('click', this.documentClickListener, true);
|
|
8265
|
+
document.addEventListener('keydown', this.escapeKeyListener, true);
|
|
8293
8266
|
}
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
request.resolve(resolvedValue);
|
|
8303
|
-
this.removeCurrentRequest();
|
|
8267
|
+
registerDropdown(dropdown) {
|
|
8268
|
+
// Close any existing dropdowns before opening a new one
|
|
8269
|
+
this.closeAllDropdowns();
|
|
8270
|
+
this.activeDropdowns.update(map => {
|
|
8271
|
+
const newMap = new Map(map);
|
|
8272
|
+
newMap.set(dropdown.id, dropdown);
|
|
8273
|
+
return newMap;
|
|
8274
|
+
});
|
|
8304
8275
|
}
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8276
|
+
unregisterDropdown(id) {
|
|
8277
|
+
this.activeDropdowns.update(map => {
|
|
8278
|
+
const newMap = new Map(map);
|
|
8279
|
+
newMap.delete(id);
|
|
8280
|
+
return newMap;
|
|
8281
|
+
});
|
|
8311
8282
|
}
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8283
|
+
closeAllDropdowns() {
|
|
8284
|
+
const dropdowns = this.activeDropdowns();
|
|
8285
|
+
dropdowns.forEach(dropdown => {
|
|
8286
|
+
dropdown.close();
|
|
8287
|
+
});
|
|
8288
|
+
this.activeDropdowns.set(new Map());
|
|
8317
8289
|
}
|
|
8318
|
-
|
|
8319
|
-
|
|
8290
|
+
closeDropdown(id) {
|
|
8291
|
+
const dropdowns = this.activeDropdowns();
|
|
8292
|
+
const dropdown = dropdowns.get(id);
|
|
8293
|
+
if (dropdown) {
|
|
8294
|
+
dropdown.close();
|
|
8295
|
+
this.unregisterDropdown(id);
|
|
8296
|
+
}
|
|
8320
8297
|
}
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
const iconMap = {
|
|
8324
|
-
danger: 'error',
|
|
8325
|
-
warning: 'warning',
|
|
8326
|
-
info: 'info',
|
|
8327
|
-
success: 'check_circle'
|
|
8328
|
-
};
|
|
8329
|
-
return iconMap[type];
|
|
8298
|
+
isDropdownOpen(id) {
|
|
8299
|
+
return this.activeDropdowns().has(id);
|
|
8330
8300
|
}
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8301
|
+
handleDocumentClick(event) {
|
|
8302
|
+
const dropdowns = this.activeDropdowns();
|
|
8303
|
+
if (dropdowns.size === 0) {
|
|
8304
|
+
return;
|
|
8305
|
+
}
|
|
8306
|
+
// Check if click is inside any open dropdown
|
|
8307
|
+
const target = event.target;
|
|
8308
|
+
let isClickInsideDropdown = false;
|
|
8309
|
+
dropdowns.forEach(dropdown => {
|
|
8310
|
+
if (dropdown.element && dropdown.element.contains(target)) {
|
|
8311
|
+
isClickInsideDropdown = true;
|
|
8312
|
+
}
|
|
8313
|
+
});
|
|
8314
|
+
// If click is outside all dropdowns, close them
|
|
8315
|
+
if (!isClickInsideDropdown) {
|
|
8316
|
+
this.closeAllDropdowns();
|
|
8317
|
+
}
|
|
8334
8318
|
}
|
|
8335
|
-
|
|
8336
|
-
|
|
8319
|
+
ngOnDestroy() {
|
|
8320
|
+
// Clean up global listeners
|
|
8321
|
+
if (this.documentClickListener) {
|
|
8322
|
+
document.removeEventListener('click', this.documentClickListener, true);
|
|
8323
|
+
}
|
|
8324
|
+
if (this.escapeKeyListener) {
|
|
8325
|
+
document.removeEventListener('keydown', this.escapeKeyListener, true);
|
|
8326
|
+
}
|
|
8337
8327
|
}
|
|
8338
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
8339
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
8328
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: DropdownManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8329
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: DropdownManagerService, providedIn: 'root' });
|
|
8340
8330
|
}
|
|
8341
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
8331
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: DropdownManagerService, decorators: [{
|
|
8342
8332
|
type: Injectable,
|
|
8343
8333
|
args: [{
|
|
8344
8334
|
providedIn: 'root'
|
|
8345
8335
|
}]
|
|
8346
8336
|
}], ctorParameters: () => [] });
|
|
8347
8337
|
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
*/
|
|
8353
|
-
class CideThemeService {
|
|
8354
|
-
platformId = inject(PLATFORM_ID);
|
|
8355
|
-
isBrowser;
|
|
8356
|
-
currentTheme$ = new BehaviorSubject('light');
|
|
8357
|
-
effectiveTheme$ = new BehaviorSubject('light');
|
|
8358
|
-
config = {
|
|
8359
|
-
theme: 'light',
|
|
8360
|
-
targetElement: null,
|
|
8361
|
-
persistPreference: true,
|
|
8362
|
-
storageKey: 'cide-theme',
|
|
8363
|
-
useDataAttribute: true,
|
|
8364
|
-
useClassName: true
|
|
8365
|
-
};
|
|
8338
|
+
class KeyboardShortcutService {
|
|
8339
|
+
shortcuts = new Map();
|
|
8340
|
+
overrides = new Map();
|
|
8341
|
+
keydownListener;
|
|
8366
8342
|
constructor() {
|
|
8367
|
-
this.
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8343
|
+
this.setupGlobalListener();
|
|
8344
|
+
}
|
|
8345
|
+
ngOnDestroy() {
|
|
8346
|
+
this.removeGlobalListener();
|
|
8371
8347
|
}
|
|
8372
8348
|
/**
|
|
8373
|
-
*
|
|
8374
|
-
* This should be called once in the application, typically in main.ts or app config
|
|
8349
|
+
* Register a new keyboard shortcut
|
|
8375
8350
|
*/
|
|
8376
|
-
|
|
8377
|
-
|
|
8351
|
+
register(shortcut) {
|
|
8352
|
+
this.shortcuts.set(shortcut.id, shortcut);
|
|
8353
|
+
console.log(`⌨️ [KeyboardShortcut] Registered shortcut: ${shortcut.id} (${this.getKeyDescription(shortcut)})`);
|
|
8354
|
+
}
|
|
8355
|
+
/**
|
|
8356
|
+
* Override an existing shortcut with new key combination
|
|
8357
|
+
*/
|
|
8358
|
+
override(shortcutId, newKey, options) {
|
|
8359
|
+
const originalShortcut = this.shortcuts.get(shortcutId);
|
|
8360
|
+
if (!originalShortcut) {
|
|
8361
|
+
console.warn(`⚠️ [KeyboardShortcut] Cannot override shortcut '${shortcutId}' - not found`);
|
|
8378
8362
|
return;
|
|
8379
8363
|
}
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
}
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
const initialTheme = savedTheme || this.config.theme;
|
|
8391
|
-
// Apply initial theme
|
|
8392
|
-
this.setTheme(initialTheme);
|
|
8393
|
-
// Listen for system theme changes if theme is set to 'auto'
|
|
8394
|
-
if (initialTheme === 'auto') {
|
|
8395
|
-
this.listenToSystemTheme();
|
|
8396
|
-
}
|
|
8364
|
+
const override = {
|
|
8365
|
+
shortcutId,
|
|
8366
|
+
newKey,
|
|
8367
|
+
newCtrlKey: options?.ctrlKey,
|
|
8368
|
+
newAltKey: options?.altKey,
|
|
8369
|
+
newShiftKey: options?.shiftKey,
|
|
8370
|
+
newMetaKey: options?.metaKey
|
|
8371
|
+
};
|
|
8372
|
+
this.overrides.set(shortcutId, override);
|
|
8373
|
+
console.log(`🔄 [KeyboardShortcut] Override registered for '${shortcutId}': ${this.getKeyDescription({ ...originalShortcut, ...options, key: newKey })}`);
|
|
8397
8374
|
}
|
|
8398
8375
|
/**
|
|
8399
|
-
*
|
|
8376
|
+
* Remove a shortcut
|
|
8400
8377
|
*/
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
}
|
|
8405
|
-
this.currentTheme$.next(theme);
|
|
8406
|
-
// Save preference if persistence is enabled
|
|
8407
|
-
if (this.config.persistPreference) {
|
|
8408
|
-
this.saveThemePreference(theme);
|
|
8409
|
-
}
|
|
8410
|
-
// Determine effective theme
|
|
8411
|
-
let effectiveTheme;
|
|
8412
|
-
if (theme === 'auto') {
|
|
8413
|
-
effectiveTheme = this.getSystemTheme();
|
|
8414
|
-
this.listenToSystemTheme();
|
|
8415
|
-
}
|
|
8416
|
-
else {
|
|
8417
|
-
effectiveTheme = theme;
|
|
8418
|
-
}
|
|
8419
|
-
this.effectiveTheme$.next(effectiveTheme);
|
|
8420
|
-
this.applyTheme(effectiveTheme);
|
|
8378
|
+
unregister(shortcutId) {
|
|
8379
|
+
this.shortcuts.delete(shortcutId);
|
|
8380
|
+
this.overrides.delete(shortcutId);
|
|
8381
|
+
console.log(`🗑️ [KeyboardShortcut] Removed shortcut: ${shortcutId}`);
|
|
8421
8382
|
}
|
|
8422
8383
|
/**
|
|
8423
|
-
*
|
|
8384
|
+
* Remove override for a shortcut (restore original key combination)
|
|
8424
8385
|
*/
|
|
8425
|
-
|
|
8426
|
-
|
|
8386
|
+
removeOverride(shortcutId) {
|
|
8387
|
+
this.overrides.delete(shortcutId);
|
|
8388
|
+
console.log(`🔄 [KeyboardShortcut] Override removed for: ${shortcutId}`);
|
|
8427
8389
|
}
|
|
8428
8390
|
/**
|
|
8429
|
-
* Get
|
|
8391
|
+
* Get all registered shortcuts
|
|
8430
8392
|
*/
|
|
8431
|
-
|
|
8432
|
-
return this.
|
|
8393
|
+
getAllShortcuts() {
|
|
8394
|
+
return Array.from(this.shortcuts.values());
|
|
8433
8395
|
}
|
|
8434
8396
|
/**
|
|
8435
|
-
*
|
|
8397
|
+
* Get shortcuts by category or filter
|
|
8436
8398
|
*/
|
|
8437
|
-
|
|
8438
|
-
|
|
8399
|
+
getShortcuts(filter) {
|
|
8400
|
+
const shortcuts = this.getAllShortcuts();
|
|
8401
|
+
return filter ? shortcuts.filter(filter) : shortcuts;
|
|
8439
8402
|
}
|
|
8440
8403
|
/**
|
|
8441
|
-
*
|
|
8404
|
+
* Check if a shortcut is registered
|
|
8442
8405
|
*/
|
|
8443
|
-
|
|
8444
|
-
return this.
|
|
8406
|
+
hasShortcut(shortcutId) {
|
|
8407
|
+
return this.shortcuts.has(shortcutId);
|
|
8445
8408
|
}
|
|
8446
8409
|
/**
|
|
8447
|
-
*
|
|
8410
|
+
* Get shortcut information
|
|
8448
8411
|
*/
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
const newTheme = currentEffective === 'light' ? 'dark' : 'light';
|
|
8452
|
-
this.setTheme(newTheme);
|
|
8412
|
+
getShortcut(shortcutId) {
|
|
8413
|
+
return this.shortcuts.get(shortcutId);
|
|
8453
8414
|
}
|
|
8454
8415
|
/**
|
|
8455
|
-
*
|
|
8416
|
+
* Clear all shortcuts
|
|
8456
8417
|
*/
|
|
8457
|
-
|
|
8458
|
-
|
|
8418
|
+
clearAll() {
|
|
8419
|
+
this.shortcuts.clear();
|
|
8420
|
+
this.overrides.clear();
|
|
8421
|
+
console.log(`🧹 [KeyboardShortcut] All shortcuts cleared`);
|
|
8459
8422
|
}
|
|
8460
8423
|
/**
|
|
8461
|
-
*
|
|
8424
|
+
* Set up global keyboard listener
|
|
8462
8425
|
*/
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
}
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
else {
|
|
8479
|
-
element.classList.add('light-mode');
|
|
8480
|
-
element.classList.remove('dark-mode');
|
|
8481
|
-
}
|
|
8426
|
+
setupGlobalListener() {
|
|
8427
|
+
this.keydownListener = (event) => {
|
|
8428
|
+
this.handleKeydown(event);
|
|
8429
|
+
};
|
|
8430
|
+
document.addEventListener('keydown', this.keydownListener);
|
|
8431
|
+
console.log(`🎧 [KeyboardShortcut] Global keyboard listener attached`);
|
|
8432
|
+
}
|
|
8433
|
+
/**
|
|
8434
|
+
* Remove global keyboard listener
|
|
8435
|
+
*/
|
|
8436
|
+
removeGlobalListener() {
|
|
8437
|
+
if (this.keydownListener) {
|
|
8438
|
+
document.removeEventListener('keydown', this.keydownListener);
|
|
8439
|
+
this.keydownListener = undefined;
|
|
8440
|
+
console.log(`🎧 [KeyboardShortcut] Global keyboard listener removed`);
|
|
8482
8441
|
}
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8442
|
+
}
|
|
8443
|
+
/**
|
|
8444
|
+
* Handle keydown events
|
|
8445
|
+
*/
|
|
8446
|
+
handleKeydown(event) {
|
|
8447
|
+
for (const [shortcutId, shortcut] of this.shortcuts) {
|
|
8448
|
+
if (this.matchesShortcut(event, shortcut)) {
|
|
8449
|
+
// Check for override
|
|
8450
|
+
const override = this.overrides.get(shortcutId);
|
|
8451
|
+
if (override && !this.matchesOverride(event, override)) {
|
|
8452
|
+
continue; // Skip if override doesn't match
|
|
8492
8453
|
}
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
document.body.classList.remove('dark-mode');
|
|
8454
|
+
if (shortcut.preventDefault !== false) {
|
|
8455
|
+
event.preventDefault();
|
|
8496
8456
|
}
|
|
8457
|
+
if (shortcut.stopPropagation) {
|
|
8458
|
+
event.stopPropagation();
|
|
8459
|
+
}
|
|
8460
|
+
console.log(`⌨️ [KeyboardShortcut] Executing shortcut: ${shortcutId}`);
|
|
8461
|
+
shortcut.action();
|
|
8462
|
+
break; // Only execute one shortcut per keydown
|
|
8497
8463
|
}
|
|
8498
8464
|
}
|
|
8499
8465
|
}
|
|
8500
8466
|
/**
|
|
8501
|
-
*
|
|
8467
|
+
* Check if event matches shortcut
|
|
8502
8468
|
*/
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8469
|
+
matchesShortcut(event, shortcut) {
|
|
8470
|
+
return event.key === shortcut.key &&
|
|
8471
|
+
(shortcut.ctrlKey === undefined || event.ctrlKey === shortcut.ctrlKey) &&
|
|
8472
|
+
(shortcut.altKey === undefined || event.altKey === shortcut.altKey) &&
|
|
8473
|
+
(shortcut.shiftKey === undefined || event.shiftKey === shortcut.shiftKey) &&
|
|
8474
|
+
(shortcut.metaKey === undefined || event.metaKey === shortcut.metaKey);
|
|
8475
|
+
}
|
|
8476
|
+
/**
|
|
8477
|
+
* Check if event matches override
|
|
8478
|
+
*/
|
|
8479
|
+
matchesOverride(event, override) {
|
|
8480
|
+
return event.key === override.newKey &&
|
|
8481
|
+
(override.newCtrlKey === undefined || event.ctrlKey === override.newCtrlKey) &&
|
|
8482
|
+
(override.newAltKey === undefined || event.altKey === override.newAltKey) &&
|
|
8483
|
+
(override.newShiftKey === undefined || event.shiftKey === override.newShiftKey) &&
|
|
8484
|
+
(override.newMetaKey === undefined || event.metaKey === override.newMetaKey);
|
|
8485
|
+
}
|
|
8486
|
+
/**
|
|
8487
|
+
* Get human-readable key description
|
|
8488
|
+
*/
|
|
8489
|
+
getKeyDescription(shortcut) {
|
|
8490
|
+
const modifiers = [];
|
|
8491
|
+
if (shortcut.ctrlKey)
|
|
8492
|
+
modifiers.push('Ctrl');
|
|
8493
|
+
if (shortcut.altKey)
|
|
8494
|
+
modifiers.push('Alt');
|
|
8495
|
+
if (shortcut.shiftKey)
|
|
8496
|
+
modifiers.push('Shift');
|
|
8497
|
+
if (shortcut.metaKey)
|
|
8498
|
+
modifiers.push('Meta');
|
|
8499
|
+
return [...modifiers, shortcut.key].join(' + ');
|
|
8500
|
+
}
|
|
8501
|
+
/**
|
|
8502
|
+
* Get key description for a shortcut ID
|
|
8503
|
+
*/
|
|
8504
|
+
getKeyDescriptionForShortcut(shortcutId) {
|
|
8505
|
+
const shortcut = this.shortcuts.get(shortcutId);
|
|
8506
|
+
if (!shortcut)
|
|
8507
|
+
return 'Not found';
|
|
8508
|
+
const override = this.overrides.get(shortcutId);
|
|
8509
|
+
if (override) {
|
|
8510
|
+
return this.getKeyDescription({
|
|
8511
|
+
key: override.newKey,
|
|
8512
|
+
ctrlKey: override.newCtrlKey,
|
|
8513
|
+
altKey: override.newAltKey,
|
|
8514
|
+
shiftKey: override.newShiftKey,
|
|
8515
|
+
metaKey: override.newMetaKey
|
|
8516
|
+
});
|
|
8509
8517
|
}
|
|
8510
|
-
return
|
|
8518
|
+
return this.getKeyDescription(shortcut);
|
|
8519
|
+
}
|
|
8520
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: KeyboardShortcutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8521
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: KeyboardShortcutService, providedIn: 'root' });
|
|
8522
|
+
}
|
|
8523
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: KeyboardShortcutService, decorators: [{
|
|
8524
|
+
type: Injectable,
|
|
8525
|
+
args: [{
|
|
8526
|
+
providedIn: 'root'
|
|
8527
|
+
}]
|
|
8528
|
+
}], ctorParameters: () => [] });
|
|
8529
|
+
|
|
8530
|
+
class ConfirmationService {
|
|
8531
|
+
// Modern signal-based state management
|
|
8532
|
+
confirmationQueue = signal([], ...(ngDevMode ? [{ debugName: "confirmationQueue" }] : []));
|
|
8533
|
+
currentRequest = signal(null, ...(ngDevMode ? [{ debugName: "currentRequest" }] : []));
|
|
8534
|
+
// Modern computed signals with proper typing
|
|
8535
|
+
hasActiveConfirmation = computed(() => this.currentRequest() !== null, ...(ngDevMode ? [{ debugName: "hasActiveConfirmation" }] : []));
|
|
8536
|
+
queueLength = computed(() => this.confirmationQueue().length, ...(ngDevMode ? [{ debugName: "queueLength" }] : []));
|
|
8537
|
+
constructor() {
|
|
8538
|
+
// Modern service initialization
|
|
8539
|
+
}
|
|
8540
|
+
ngOnDestroy() {
|
|
8541
|
+
// Cleanup if needed
|
|
8542
|
+
}
|
|
8543
|
+
/**
|
|
8544
|
+
* Ask for confirmation with a simple yes/no dialog
|
|
8545
|
+
*/
|
|
8546
|
+
ask(options) {
|
|
8547
|
+
console.log('🔔 ConfirmationService.ask called with options:', options);
|
|
8548
|
+
return new Promise((resolve, reject) => {
|
|
8549
|
+
const request = {
|
|
8550
|
+
id: this.generateId(),
|
|
8551
|
+
title: options.title,
|
|
8552
|
+
message: options.message,
|
|
8553
|
+
type: options.type || 'warning',
|
|
8554
|
+
icon: options.icon || this.getDefaultIcon(options.type || 'warning'),
|
|
8555
|
+
confirmText: options.confirmText || 'Confirm',
|
|
8556
|
+
cancelText: options.cancelText || 'Cancel',
|
|
8557
|
+
resolve: (value) => resolve(value),
|
|
8558
|
+
reject
|
|
8559
|
+
};
|
|
8560
|
+
console.log('🔽 Ask for confirmation with a simple yes/no dialog', request);
|
|
8561
|
+
this.addToQueue(request);
|
|
8562
|
+
});
|
|
8563
|
+
}
|
|
8564
|
+
/**
|
|
8565
|
+
* Ask for confirmation with custom template and return custom data
|
|
8566
|
+
*/
|
|
8567
|
+
askWithTemplate(options) {
|
|
8568
|
+
return new Promise((resolve, reject) => {
|
|
8569
|
+
const request = {
|
|
8570
|
+
id: this.generateId(),
|
|
8571
|
+
title: options.title,
|
|
8572
|
+
message: options.message,
|
|
8573
|
+
type: options.type || 'info',
|
|
8574
|
+
icon: options.icon || this.getDefaultIcon(options.type || 'info'),
|
|
8575
|
+
confirmText: options.confirmText || 'Confirm',
|
|
8576
|
+
cancelText: options.cancelText || 'Cancel',
|
|
8577
|
+
customTemplate: options.customTemplate,
|
|
8578
|
+
customData: options.customData,
|
|
8579
|
+
resolve: (value) => resolve(value),
|
|
8580
|
+
reject
|
|
8581
|
+
};
|
|
8582
|
+
// Cast to unknown for queue compatibility
|
|
8583
|
+
this.addToQueue(request);
|
|
8584
|
+
});
|
|
8511
8585
|
}
|
|
8512
8586
|
/**
|
|
8513
|
-
*
|
|
8587
|
+
* Quick confirmation for dangerous actions
|
|
8514
8588
|
*/
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
this.applyTheme(effectiveTheme);
|
|
8525
|
-
}
|
|
8526
|
-
};
|
|
8527
|
-
// Modern browsers
|
|
8528
|
-
if (mediaQuery.addEventListener) {
|
|
8529
|
-
mediaQuery.addEventListener('change', handler);
|
|
8530
|
-
}
|
|
8531
|
-
else if (mediaQuery.addListener) {
|
|
8532
|
-
// Legacy browsers
|
|
8533
|
-
mediaQuery.addListener(handler);
|
|
8534
|
-
}
|
|
8589
|
+
confirmDelete(itemName) {
|
|
8590
|
+
return this.ask({
|
|
8591
|
+
title: 'Confirm Delete',
|
|
8592
|
+
message: itemName ? `Are you sure you want to delete "${itemName}"? This action cannot be undone.` : 'Are you sure you want to delete this item? This action cannot be undone.',
|
|
8593
|
+
type: 'danger',
|
|
8594
|
+
icon: 'delete_forever',
|
|
8595
|
+
confirmText: 'Delete',
|
|
8596
|
+
cancelText: 'Cancel'
|
|
8597
|
+
});
|
|
8535
8598
|
}
|
|
8536
8599
|
/**
|
|
8537
|
-
*
|
|
8600
|
+
* Quick confirmation for permanent actions
|
|
8538
8601
|
*/
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
if (theme === 'dark') {
|
|
8549
|
-
localStorage.setItem(this.config.storageKey, 'dark');
|
|
8550
|
-
}
|
|
8551
|
-
else if (theme === 'light') {
|
|
8552
|
-
localStorage.setItem(this.config.storageKey, 'light');
|
|
8553
|
-
}
|
|
8554
|
-
}
|
|
8555
|
-
catch (error) {
|
|
8556
|
-
console.warn('Failed to save theme preference to localStorage:', error);
|
|
8557
|
-
}
|
|
8602
|
+
confirmPermanentAction(action, itemName) {
|
|
8603
|
+
return this.ask({
|
|
8604
|
+
title: `Confirm ${action}`,
|
|
8605
|
+
message: itemName ? `Are you sure you want to ${action.toLowerCase()} "${itemName}"? This action cannot be undone.` : `Are you sure you want to ${action.toLowerCase()}? This action cannot be undone.`,
|
|
8606
|
+
type: 'danger',
|
|
8607
|
+
icon: 'warning',
|
|
8608
|
+
confirmText: action,
|
|
8609
|
+
cancelText: 'Cancel'
|
|
8610
|
+
});
|
|
8558
8611
|
}
|
|
8559
8612
|
/**
|
|
8560
|
-
*
|
|
8613
|
+
* Quick confirmation for safe actions
|
|
8561
8614
|
*/
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
}
|
|
8572
|
-
// Handle legacy format: 'true' (dark mode) or 'false' (light mode)
|
|
8573
|
-
if (saved === 'true') {
|
|
8574
|
-
// Migrate to new format
|
|
8575
|
-
localStorage.setItem(this.config.storageKey, 'dark');
|
|
8576
|
-
return 'dark';
|
|
8577
|
-
}
|
|
8578
|
-
else if (saved === 'false') {
|
|
8579
|
-
// Migrate to new format
|
|
8580
|
-
localStorage.setItem(this.config.storageKey, 'light');
|
|
8581
|
-
return 'light';
|
|
8582
|
-
}
|
|
8583
|
-
}
|
|
8584
|
-
catch (error) {
|
|
8585
|
-
console.warn('Failed to load theme preference from localStorage:', error);
|
|
8586
|
-
}
|
|
8587
|
-
return null;
|
|
8615
|
+
confirmSafeAction(action, itemName) {
|
|
8616
|
+
return this.ask({
|
|
8617
|
+
title: `Confirm ${action}`,
|
|
8618
|
+
message: itemName ? `Are you sure you want to ${action.toLowerCase()} "${itemName}"?` : `Are you sure you want to ${action.toLowerCase()}?`,
|
|
8619
|
+
type: 'info',
|
|
8620
|
+
icon: 'info',
|
|
8621
|
+
confirmText: action,
|
|
8622
|
+
cancelText: 'Cancel'
|
|
8623
|
+
});
|
|
8588
8624
|
}
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8625
|
+
// Modern private methods with proper typing
|
|
8626
|
+
addToQueue(request) {
|
|
8627
|
+
this.confirmationQueue.update(queue => [...queue, request]);
|
|
8628
|
+
console.log('🔔 Added request to queue:', request);
|
|
8629
|
+
this.processQueue();
|
|
8630
|
+
}
|
|
8631
|
+
processQueue() {
|
|
8632
|
+
console.log('🔔 Processing queue. Current request:', this.currentRequest(), 'Queue length:', this.confirmationQueue().length);
|
|
8633
|
+
if (this.currentRequest() || this.confirmationQueue().length === 0) {
|
|
8594
8634
|
return;
|
|
8595
8635
|
}
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
catch (error) {
|
|
8600
|
-
console.warn('Failed to clear theme preference from localStorage:', error);
|
|
8601
|
-
}
|
|
8636
|
+
const nextRequest = this.confirmationQueue()[0];
|
|
8637
|
+
console.log('🔔 Setting current request:', nextRequest);
|
|
8638
|
+
this.currentRequest.set(nextRequest);
|
|
8602
8639
|
}
|
|
8603
|
-
|
|
8604
|
-
|
|
8640
|
+
// Modern public methods with proper typing
|
|
8641
|
+
confirmCurrentRequest(data) {
|
|
8642
|
+
const request = this.currentRequest();
|
|
8643
|
+
if (!request)
|
|
8644
|
+
return;
|
|
8645
|
+
// Always resolve with true for confirmation, unless custom data is explicitly provided
|
|
8646
|
+
const resolvedValue = data !== undefined ? data : true;
|
|
8647
|
+
console.log('🔔 Confirming request with value:', resolvedValue);
|
|
8648
|
+
request.resolve(resolvedValue);
|
|
8649
|
+
this.removeCurrentRequest();
|
|
8650
|
+
}
|
|
8651
|
+
cancelCurrentRequest() {
|
|
8652
|
+
const request = this.currentRequest();
|
|
8653
|
+
if (!request)
|
|
8654
|
+
return;
|
|
8655
|
+
request.reject(new Error('User cancelled'));
|
|
8656
|
+
this.removeCurrentRequest();
|
|
8657
|
+
}
|
|
8658
|
+
removeCurrentRequest() {
|
|
8659
|
+
this.confirmationQueue.update(queue => queue.slice(1));
|
|
8660
|
+
this.currentRequest.set(null);
|
|
8661
|
+
// Process next request if any
|
|
8662
|
+
setTimeout(() => this.processQueue(), 100);
|
|
8663
|
+
}
|
|
8664
|
+
generateId() {
|
|
8665
|
+
return `confirmation-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
8666
|
+
}
|
|
8667
|
+
// Modern private utility method with proper typing
|
|
8668
|
+
getDefaultIcon(type) {
|
|
8669
|
+
const iconMap = {
|
|
8670
|
+
danger: 'error',
|
|
8671
|
+
warning: 'warning',
|
|
8672
|
+
info: 'info',
|
|
8673
|
+
success: 'check_circle'
|
|
8674
|
+
};
|
|
8675
|
+
return iconMap[type];
|
|
8676
|
+
}
|
|
8677
|
+
// Modern public getters with proper typing
|
|
8678
|
+
getCurrentRequest() {
|
|
8679
|
+
return this.currentRequest;
|
|
8680
|
+
}
|
|
8681
|
+
getHasActiveConfirmation() {
|
|
8682
|
+
return this.hasActiveConfirmation;
|
|
8683
|
+
}
|
|
8684
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ConfirmationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8685
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ConfirmationService, providedIn: 'root' });
|
|
8605
8686
|
}
|
|
8606
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type:
|
|
8687
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ConfirmationService, decorators: [{
|
|
8607
8688
|
type: Injectable,
|
|
8608
8689
|
args: [{
|
|
8609
8690
|
providedIn: 'root'
|
|
@@ -9024,7 +9105,7 @@ class CideEleConfirmationModalComponent {
|
|
|
9024
9105
|
</button>
|
|
9025
9106
|
</div>
|
|
9026
9107
|
</div>
|
|
9027
|
-
`, isInline: true, styles: [".confirmation-backdrop{position:fixed;inset:0;background-color:#
|
|
9108
|
+
`, isInline: true, styles: [".confirmation-backdrop{position:fixed;inset:0;background-color:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:10000;display:flex;align-items:center;justify-content:center}[data-theme=dark] .confirmation-backdrop,.dark-mode .confirmation-backdrop{background-color:#000c}.confirmation-modal{background:var(--cide-ele-bg-primary, #ffffff);border-radius:12px;box-shadow:var(--cide-ele-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04));max-width:480px;width:90%;max-height:90vh;overflow:hidden;z-index:10001;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);border:1px solid var(--cide-ele-border-primary, #e5e7eb)}.modal-header{padding:24px 24px 16px;border-bottom:1px solid var(--cide-ele-border-primary, #e5e7eb)}.header-content{display:flex;align-items:center;gap:16px}.icon-container{width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.icon-container.danger{background-color:var(--cide-ele-error-light, #fef2f2);color:var(--cide-ele-error, #dc2626)}.icon-container.warning{background-color:var(--cide-ele-warning-light, #fffbeb);color:var(--cide-ele-warning, #d97706)}.icon-container.info{background-color:var(--cide-ele-info-light, #eff6ff);color:var(--cide-ele-info, #2563eb)}.icon-container.success{background-color:var(--cide-ele-success-light, #f0fdf4);color:var(--cide-ele-success, #16a34a)}.modal-title{margin:0;font-size:20px;font-weight:600;color:var(--cide-ele-text-primary, #111827);line-height:1.4}.modal-body{padding:16px 24px 24px}.modal-message{margin:0 0 16px;font-size:16px;line-height:1.5;color:var(--cide-ele-text-secondary, #6b7280)}.custom-content{margin-top:16px;padding-top:16px;border-top:1px solid var(--cide-ele-border-primary, #f3f4f6)}.modal-footer{padding:16px 24px 24px;display:flex;gap:12px;justify-content:flex-end;border-top:1px solid var(--cide-ele-border-primary, #e5e7eb)}.btn{padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .2s ease;min-width:80px}.btn:hover{transform:translateY(-1px)}.btn:active{transform:translateY(0)}.btn-secondary{background-color:var(--cide-ele-bg-secondary, #f9fafb);color:var(--cide-ele-text-primary, #374151);border:1px solid var(--cide-ele-border-secondary, #d1d5db)}.btn-secondary:hover{background-color:var(--cide-ele-bg-hover, #f3f4f6);border-color:var(--cide-ele-border-hover, #9ca3af)}.btn-primary{background-color:var(--cide-ele-brand-primary, #2563eb);color:#fff}.btn-primary:hover{background-color:var(--cide-ele-brand-primary-hover, #1d4ed8)}.btn-danger{background-color:var(--cide-ele-error, #dc2626);color:#fff}.btn-danger:hover{background-color:#b91c1c}.btn-warning{background-color:var(--cide-ele-warning, #d97706);color:#fff}.btn-warning:hover{background-color:#b45309}.btn-success{background-color:var(--cide-ele-success, #16a34a);color:#fff}.btn-success:hover{background-color:#15803d}@media (max-width: 640px){.confirmation-modal{width:95%;margin:20px}.modal-header{padding:20px 20px 12px}.modal-body{padding:12px 20px 20px}.modal-footer{padding:12px 20px 20px;flex-direction:column-reverse}.btn{width:100%;justify-content:center}.header-content{flex-direction:column;text-align:center;gap:12px}.modal-title{font-size:18px}}.btn:focus{outline:2px solid var(--cide-ele-brand-primary, #2563eb);outline-offset:2px}.modal-enter{opacity:0;transform:scale(.95) translateY(-10px)}.modal-enter-active{opacity:1;transform:scale(1) translateY(0);transition:all .2s ease-out}.modal-exit{opacity:1;transform:scale(1) translateY(0)}.modal-exit-active{opacity:0;transform:scale(.95) translateY(-10px);transition:all .15s ease-in}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
9028
9109
|
}
|
|
9029
9110
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleConfirmationModalComponent, decorators: [{
|
|
9030
9111
|
type: Component,
|
|
@@ -9085,7 +9166,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
9085
9166
|
</button>
|
|
9086
9167
|
</div>
|
|
9087
9168
|
</div>
|
|
9088
|
-
`, styles: [".confirmation-backdrop{position:fixed;inset:0;background-color:#
|
|
9169
|
+
`, styles: [".confirmation-backdrop{position:fixed;inset:0;background-color:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:10000;display:flex;align-items:center;justify-content:center}[data-theme=dark] .confirmation-backdrop,.dark-mode .confirmation-backdrop{background-color:#000c}.confirmation-modal{background:var(--cide-ele-bg-primary, #ffffff);border-radius:12px;box-shadow:var(--cide-ele-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04));max-width:480px;width:90%;max-height:90vh;overflow:hidden;z-index:10001;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);border:1px solid var(--cide-ele-border-primary, #e5e7eb)}.modal-header{padding:24px 24px 16px;border-bottom:1px solid var(--cide-ele-border-primary, #e5e7eb)}.header-content{display:flex;align-items:center;gap:16px}.icon-container{width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.icon-container.danger{background-color:var(--cide-ele-error-light, #fef2f2);color:var(--cide-ele-error, #dc2626)}.icon-container.warning{background-color:var(--cide-ele-warning-light, #fffbeb);color:var(--cide-ele-warning, #d97706)}.icon-container.info{background-color:var(--cide-ele-info-light, #eff6ff);color:var(--cide-ele-info, #2563eb)}.icon-container.success{background-color:var(--cide-ele-success-light, #f0fdf4);color:var(--cide-ele-success, #16a34a)}.modal-title{margin:0;font-size:20px;font-weight:600;color:var(--cide-ele-text-primary, #111827);line-height:1.4}.modal-body{padding:16px 24px 24px}.modal-message{margin:0 0 16px;font-size:16px;line-height:1.5;color:var(--cide-ele-text-secondary, #6b7280)}.custom-content{margin-top:16px;padding-top:16px;border-top:1px solid var(--cide-ele-border-primary, #f3f4f6)}.modal-footer{padding:16px 24px 24px;display:flex;gap:12px;justify-content:flex-end;border-top:1px solid var(--cide-ele-border-primary, #e5e7eb)}.btn{padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .2s ease;min-width:80px}.btn:hover{transform:translateY(-1px)}.btn:active{transform:translateY(0)}.btn-secondary{background-color:var(--cide-ele-bg-secondary, #f9fafb);color:var(--cide-ele-text-primary, #374151);border:1px solid var(--cide-ele-border-secondary, #d1d5db)}.btn-secondary:hover{background-color:var(--cide-ele-bg-hover, #f3f4f6);border-color:var(--cide-ele-border-hover, #9ca3af)}.btn-primary{background-color:var(--cide-ele-brand-primary, #2563eb);color:#fff}.btn-primary:hover{background-color:var(--cide-ele-brand-primary-hover, #1d4ed8)}.btn-danger{background-color:var(--cide-ele-error, #dc2626);color:#fff}.btn-danger:hover{background-color:#b91c1c}.btn-warning{background-color:var(--cide-ele-warning, #d97706);color:#fff}.btn-warning:hover{background-color:#b45309}.btn-success{background-color:var(--cide-ele-success, #16a34a);color:#fff}.btn-success:hover{background-color:#15803d}@media (max-width: 640px){.confirmation-modal{width:95%;margin:20px}.modal-header{padding:20px 20px 12px}.modal-body{padding:12px 20px 20px}.modal-footer{padding:12px 20px 20px;flex-direction:column-reverse}.btn{width:100%;justify-content:center}.header-content{flex-direction:column;text-align:center;gap:12px}.modal-title{font-size:18px}}.btn:focus{outline:2px solid var(--cide-ele-brand-primary, #2563eb);outline-offset:2px}.modal-enter{opacity:0;transform:scale(.95) translateY(-10px)}.modal-enter-active{opacity:1;transform:scale(1) translateY(0);transition:all .2s ease-out}.modal-exit{opacity:1;transform:scale(1) translateY(0)}.modal-exit-active{opacity:0;transform:scale(.95) translateY(-10px);transition:all .15s ease-in}\n"] }]
|
|
9089
9170
|
}] });
|
|
9090
9171
|
|
|
9091
9172
|
class CideEleToastNotificationComponent {
|
|
@@ -9109,73 +9190,73 @@ class CideEleToastNotificationComponent {
|
|
|
9109
9190
|
return notification.id;
|
|
9110
9191
|
}
|
|
9111
9192
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleToastNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9112
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleToastNotificationComponent, isStandalone: true, selector: "cide-ele-toast-notification", ngImport: i0, template: `
|
|
9113
|
-
<!-- Toast Container -->
|
|
9114
|
-
<div class="toast-container">
|
|
9115
|
-
<div
|
|
9116
|
-
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
9117
|
-
class="toast-notification"
|
|
9118
|
-
[class]="getToastClass(notification)"
|
|
9119
|
-
[@toastAnimation]>
|
|
9120
|
-
|
|
9121
|
-
<!-- Toast Content -->
|
|
9122
|
-
<div class="toast-content">
|
|
9123
|
-
<!-- Icon -->
|
|
9124
|
-
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
9125
|
-
<cide-ele-icon size="sm">
|
|
9126
|
-
{{ notification.icon }}
|
|
9127
|
-
</cide-ele-icon>
|
|
9128
|
-
</div>
|
|
9129
|
-
|
|
9130
|
-
<!-- Text Content -->
|
|
9131
|
-
<div class="toast-text">
|
|
9132
|
-
<div *ngIf="notification.title" class="toast-title">
|
|
9133
|
-
{{ notification.title }}
|
|
9134
|
-
</div>
|
|
9135
|
-
<div class="toast-message">
|
|
9136
|
-
{{ notification.message }}
|
|
9137
|
-
</div>
|
|
9138
|
-
</div>
|
|
9139
|
-
|
|
9140
|
-
<!-- Close Button -->
|
|
9141
|
-
<button
|
|
9142
|
-
type="button"
|
|
9143
|
-
class="toast-close"
|
|
9144
|
-
(click)="removeNotification(notification.id)"
|
|
9145
|
-
aria-label="Close notification">
|
|
9146
|
-
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
9147
|
-
</button>
|
|
9148
|
-
</div>
|
|
9149
|
-
|
|
9150
|
-
<!-- Undo Button -->
|
|
9151
|
-
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
9152
|
-
<button
|
|
9153
|
-
type="button"
|
|
9154
|
-
class="toast-undo-btn"
|
|
9155
|
-
(click)="executeUndo(notification.id)">
|
|
9156
|
-
{{ notification.undoText }}
|
|
9157
|
-
</button>
|
|
9158
|
-
</div>
|
|
9159
|
-
|
|
9160
|
-
<!-- Progress Bar -->
|
|
9161
|
-
<div
|
|
9162
|
-
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
9163
|
-
class="toast-progress"
|
|
9164
|
-
[style.animation-duration]="notification.duration + 'ms'">
|
|
9165
|
-
</div>
|
|
9166
|
-
|
|
9167
|
-
<!-- Custom Progress Bar -->
|
|
9168
|
-
<div
|
|
9169
|
-
*ngIf="notification.showProgress"
|
|
9170
|
-
class="toast-custom-progress">
|
|
9171
|
-
<div
|
|
9172
|
-
class="toast-custom-progress-bar"
|
|
9173
|
-
[style.width.%]="notification.progress || 0">
|
|
9174
|
-
</div>
|
|
9175
|
-
</div>
|
|
9176
|
-
</div>
|
|
9177
|
-
</div>
|
|
9178
|
-
`, 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
|
|
9193
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleToastNotificationComponent, isStandalone: true, selector: "cide-ele-toast-notification", ngImport: i0, template: `
|
|
9194
|
+
<!-- Toast Container -->
|
|
9195
|
+
<div class="toast-container">
|
|
9196
|
+
<div
|
|
9197
|
+
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
9198
|
+
class="toast-notification"
|
|
9199
|
+
[class]="getToastClass(notification)"
|
|
9200
|
+
[@toastAnimation]>
|
|
9201
|
+
|
|
9202
|
+
<!-- Toast Content -->
|
|
9203
|
+
<div class="toast-content">
|
|
9204
|
+
<!-- Icon -->
|
|
9205
|
+
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
9206
|
+
<cide-ele-icon size="sm">
|
|
9207
|
+
{{ notification.icon }}
|
|
9208
|
+
</cide-ele-icon>
|
|
9209
|
+
</div>
|
|
9210
|
+
|
|
9211
|
+
<!-- Text Content -->
|
|
9212
|
+
<div class="toast-text">
|
|
9213
|
+
<div *ngIf="notification.title" class="toast-title">
|
|
9214
|
+
{{ notification.title }}
|
|
9215
|
+
</div>
|
|
9216
|
+
<div class="toast-message">
|
|
9217
|
+
{{ notification.message }}
|
|
9218
|
+
</div>
|
|
9219
|
+
</div>
|
|
9220
|
+
|
|
9221
|
+
<!-- Close Button -->
|
|
9222
|
+
<button
|
|
9223
|
+
type="button"
|
|
9224
|
+
class="toast-close"
|
|
9225
|
+
(click)="removeNotification(notification.id)"
|
|
9226
|
+
aria-label="Close notification">
|
|
9227
|
+
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
9228
|
+
</button>
|
|
9229
|
+
</div>
|
|
9230
|
+
|
|
9231
|
+
<!-- Undo Button -->
|
|
9232
|
+
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
9233
|
+
<button
|
|
9234
|
+
type="button"
|
|
9235
|
+
class="toast-undo-btn"
|
|
9236
|
+
(click)="executeUndo(notification.id)">
|
|
9237
|
+
{{ notification.undoText }}
|
|
9238
|
+
</button>
|
|
9239
|
+
</div>
|
|
9240
|
+
|
|
9241
|
+
<!-- Progress Bar -->
|
|
9242
|
+
<div
|
|
9243
|
+
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
9244
|
+
class="toast-progress"
|
|
9245
|
+
[style.animation-duration]="notification.duration + 'ms'">
|
|
9246
|
+
</div>
|
|
9247
|
+
|
|
9248
|
+
<!-- Custom Progress Bar -->
|
|
9249
|
+
<div
|
|
9250
|
+
*ngIf="notification.showProgress"
|
|
9251
|
+
class="toast-custom-progress">
|
|
9252
|
+
<div
|
|
9253
|
+
class="toast-custom-progress-bar"
|
|
9254
|
+
[style.width.%]="notification.progress || 0">
|
|
9255
|
+
</div>
|
|
9256
|
+
</div>
|
|
9257
|
+
</div>
|
|
9258
|
+
</div>
|
|
9259
|
+
`, 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:var(--cide-ele-bg-primary, #ffffff);border-radius:8px;box-shadow:var(--cide-ele-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05));border:1px solid var(--cide-ele-border-primary, #e5e7eb);overflow:hidden;position:relative;animation:slideInRight .3s ease-out}.toast-notification.success{border-left:4px solid var(--cide-ele-success, #16a34a)}.toast-notification.error{border-left:4px solid var(--cide-ele-error, #dc2626)}.toast-notification.warning{border-left:4px solid var(--cide-ele-warning, #d97706)}.toast-notification.info{border-left:4px solid var(--cide-ele-info, #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:var(--cide-ele-success-light, #f0fdf4);color:var(--cide-ele-success, #16a34a)}.toast-icon.error{background-color:var(--cide-ele-error-light, #fef2f2);color:var(--cide-ele-error, #dc2626)}.toast-icon.warning{background-color:var(--cide-ele-warning-light, #fffbeb);color:var(--cide-ele-warning, #d97706)}.toast-icon.info{background-color:var(--cide-ele-info-light, #eff6ff);color:var(--cide-ele-info, #2563eb)}.toast-text{flex:1;min-width:0}.toast-title{font-size:14px;font-weight:600;color:var(--cide-ele-text-primary, #111827);margin-bottom:4px;line-height:1.4}.toast-message{font-size:14px;color:var(--cide-ele-text-secondary, #6b7280);line-height:1.4}.toast-close{background:none;border:none;color:var(--cide-ele-text-tertiary, #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:var(--cide-ele-bg-hover, #f3f4f6);color:var(--cide-ele-text-secondary, #6b7280)}.toast-actions{padding:0 16px 16px;display:flex;justify-content:flex-end}.toast-undo-btn{background:none;border:none;color:var(--cide-ele-brand-primary, #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:var(--cide-ele-brand-primary-light, #eff6ff)}.toast-progress{position:absolute;bottom:0;left:0;height:3px;background-color:var(--cide-ele-border-primary, #e5e7eb);animation:progressShrink linear forwards}.toast-notification.success .toast-progress{background-color:var(--cide-ele-success, #16a34a)}.toast-notification.error .toast-progress{background-color:var(--cide-ele-error, #dc2626)}.toast-notification.warning .toast-progress{background-color:var(--cide-ele-warning, #d97706)}.toast-notification.info .toast-progress{background-color:var(--cide-ele-info, #2563eb)}.toast-custom-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background-color:var(--cide-ele-border-primary, #e5e7eb);overflow:hidden}.toast-custom-progress-bar{height:100%;background-color:var(--cide-ele-info, #2563eb);transition:width .3s ease;border-radius:0 3px 0 0}.toast-notification.success .toast-custom-progress-bar{background-color:var(--cide-ele-success, #16a34a)}.toast-notification.error .toast-custom-progress-bar{background-color:var(--cide-ele-error, #dc2626)}.toast-notification.warning .toast-custom-progress-bar{background-color:var(--cide-ele-warning, #d97706)}.toast-notification.info .toast-custom-progress-bar{background-color:var(--cide-ele-info, #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 var(--cide-ele-brand-primary, #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: [
|
|
9179
9260
|
trigger('toastAnimation', [
|
|
9180
9261
|
state('void', style({
|
|
9181
9262
|
transform: 'translateX(100%)',
|
|
@@ -9192,72 +9273,72 @@ class CideEleToastNotificationComponent {
|
|
|
9192
9273
|
}
|
|
9193
9274
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleToastNotificationComponent, decorators: [{
|
|
9194
9275
|
type: Component,
|
|
9195
|
-
args: [{ selector: 'cide-ele-toast-notification', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
9196
|
-
<!-- Toast Container -->
|
|
9197
|
-
<div class="toast-container">
|
|
9198
|
-
<div
|
|
9199
|
-
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
9200
|
-
class="toast-notification"
|
|
9201
|
-
[class]="getToastClass(notification)"
|
|
9202
|
-
[@toastAnimation]>
|
|
9203
|
-
|
|
9204
|
-
<!-- Toast Content -->
|
|
9205
|
-
<div class="toast-content">
|
|
9206
|
-
<!-- Icon -->
|
|
9207
|
-
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
9208
|
-
<cide-ele-icon size="sm">
|
|
9209
|
-
{{ notification.icon }}
|
|
9210
|
-
</cide-ele-icon>
|
|
9211
|
-
</div>
|
|
9212
|
-
|
|
9213
|
-
<!-- Text Content -->
|
|
9214
|
-
<div class="toast-text">
|
|
9215
|
-
<div *ngIf="notification.title" class="toast-title">
|
|
9216
|
-
{{ notification.title }}
|
|
9217
|
-
</div>
|
|
9218
|
-
<div class="toast-message">
|
|
9219
|
-
{{ notification.message }}
|
|
9220
|
-
</div>
|
|
9221
|
-
</div>
|
|
9222
|
-
|
|
9223
|
-
<!-- Close Button -->
|
|
9224
|
-
<button
|
|
9225
|
-
type="button"
|
|
9226
|
-
class="toast-close"
|
|
9227
|
-
(click)="removeNotification(notification.id)"
|
|
9228
|
-
aria-label="Close notification">
|
|
9229
|
-
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
9230
|
-
</button>
|
|
9231
|
-
</div>
|
|
9232
|
-
|
|
9233
|
-
<!-- Undo Button -->
|
|
9234
|
-
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
9235
|
-
<button
|
|
9236
|
-
type="button"
|
|
9237
|
-
class="toast-undo-btn"
|
|
9238
|
-
(click)="executeUndo(notification.id)">
|
|
9239
|
-
{{ notification.undoText }}
|
|
9240
|
-
</button>
|
|
9241
|
-
</div>
|
|
9242
|
-
|
|
9243
|
-
<!-- Progress Bar -->
|
|
9244
|
-
<div
|
|
9245
|
-
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
9246
|
-
class="toast-progress"
|
|
9247
|
-
[style.animation-duration]="notification.duration + 'ms'">
|
|
9248
|
-
</div>
|
|
9249
|
-
|
|
9250
|
-
<!-- Custom Progress Bar -->
|
|
9251
|
-
<div
|
|
9252
|
-
*ngIf="notification.showProgress"
|
|
9253
|
-
class="toast-custom-progress">
|
|
9254
|
-
<div
|
|
9255
|
-
class="toast-custom-progress-bar"
|
|
9256
|
-
[style.width.%]="notification.progress || 0">
|
|
9257
|
-
</div>
|
|
9258
|
-
</div>
|
|
9259
|
-
</div>
|
|
9260
|
-
</div>
|
|
9276
|
+
args: [{ selector: 'cide-ele-toast-notification', standalone: true, imports: [CommonModule, CideIconComponent], template: `
|
|
9277
|
+
<!-- Toast Container -->
|
|
9278
|
+
<div class="toast-container">
|
|
9279
|
+
<div
|
|
9280
|
+
*ngFor="let notification of activeNotifications(); trackBy: trackByNotification"
|
|
9281
|
+
class="toast-notification"
|
|
9282
|
+
[class]="getToastClass(notification)"
|
|
9283
|
+
[@toastAnimation]>
|
|
9284
|
+
|
|
9285
|
+
<!-- Toast Content -->
|
|
9286
|
+
<div class="toast-content">
|
|
9287
|
+
<!-- Icon -->
|
|
9288
|
+
<div class="toast-icon" [class]="getIconClass(notification.type)">
|
|
9289
|
+
<cide-ele-icon size="sm">
|
|
9290
|
+
{{ notification.icon }}
|
|
9291
|
+
</cide-ele-icon>
|
|
9292
|
+
</div>
|
|
9293
|
+
|
|
9294
|
+
<!-- Text Content -->
|
|
9295
|
+
<div class="toast-text">
|
|
9296
|
+
<div *ngIf="notification.title" class="toast-title">
|
|
9297
|
+
{{ notification.title }}
|
|
9298
|
+
</div>
|
|
9299
|
+
<div class="toast-message">
|
|
9300
|
+
{{ notification.message }}
|
|
9301
|
+
</div>
|
|
9302
|
+
</div>
|
|
9303
|
+
|
|
9304
|
+
<!-- Close Button -->
|
|
9305
|
+
<button
|
|
9306
|
+
type="button"
|
|
9307
|
+
class="toast-close"
|
|
9308
|
+
(click)="removeNotification(notification.id)"
|
|
9309
|
+
aria-label="Close notification">
|
|
9310
|
+
<cide-ele-icon size="xs">close</cide-ele-icon>
|
|
9311
|
+
</button>
|
|
9312
|
+
</div>
|
|
9313
|
+
|
|
9314
|
+
<!-- Undo Button -->
|
|
9315
|
+
<div *ngIf="notification.showUndo" class="toast-actions">
|
|
9316
|
+
<button
|
|
9317
|
+
type="button"
|
|
9318
|
+
class="toast-undo-btn"
|
|
9319
|
+
(click)="executeUndo(notification.id)">
|
|
9320
|
+
{{ notification.undoText }}
|
|
9321
|
+
</button>
|
|
9322
|
+
</div>
|
|
9323
|
+
|
|
9324
|
+
<!-- Progress Bar -->
|
|
9325
|
+
<div
|
|
9326
|
+
*ngIf="notification.duration > 0 && !notification.showProgress"
|
|
9327
|
+
class="toast-progress"
|
|
9328
|
+
[style.animation-duration]="notification.duration + 'ms'">
|
|
9329
|
+
</div>
|
|
9330
|
+
|
|
9331
|
+
<!-- Custom Progress Bar -->
|
|
9332
|
+
<div
|
|
9333
|
+
*ngIf="notification.showProgress"
|
|
9334
|
+
class="toast-custom-progress">
|
|
9335
|
+
<div
|
|
9336
|
+
class="toast-custom-progress-bar"
|
|
9337
|
+
[style.width.%]="notification.progress || 0">
|
|
9338
|
+
</div>
|
|
9339
|
+
</div>
|
|
9340
|
+
</div>
|
|
9341
|
+
</div>
|
|
9261
9342
|
`, animations: [
|
|
9262
9343
|
trigger('toastAnimation', [
|
|
9263
9344
|
state('void', style({
|
|
@@ -9271,7 +9352,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
9271
9352
|
transition('void => *', animate('300ms ease-out')),
|
|
9272
9353
|
transition('* => void', animate('200ms ease-in'))
|
|
9273
9354
|
])
|
|
9274
|
-
], 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
|
|
9355
|
+
], 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:var(--cide-ele-bg-primary, #ffffff);border-radius:8px;box-shadow:var(--cide-ele-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05));border:1px solid var(--cide-ele-border-primary, #e5e7eb);overflow:hidden;position:relative;animation:slideInRight .3s ease-out}.toast-notification.success{border-left:4px solid var(--cide-ele-success, #16a34a)}.toast-notification.error{border-left:4px solid var(--cide-ele-error, #dc2626)}.toast-notification.warning{border-left:4px solid var(--cide-ele-warning, #d97706)}.toast-notification.info{border-left:4px solid var(--cide-ele-info, #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:var(--cide-ele-success-light, #f0fdf4);color:var(--cide-ele-success, #16a34a)}.toast-icon.error{background-color:var(--cide-ele-error-light, #fef2f2);color:var(--cide-ele-error, #dc2626)}.toast-icon.warning{background-color:var(--cide-ele-warning-light, #fffbeb);color:var(--cide-ele-warning, #d97706)}.toast-icon.info{background-color:var(--cide-ele-info-light, #eff6ff);color:var(--cide-ele-info, #2563eb)}.toast-text{flex:1;min-width:0}.toast-title{font-size:14px;font-weight:600;color:var(--cide-ele-text-primary, #111827);margin-bottom:4px;line-height:1.4}.toast-message{font-size:14px;color:var(--cide-ele-text-secondary, #6b7280);line-height:1.4}.toast-close{background:none;border:none;color:var(--cide-ele-text-tertiary, #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:var(--cide-ele-bg-hover, #f3f4f6);color:var(--cide-ele-text-secondary, #6b7280)}.toast-actions{padding:0 16px 16px;display:flex;justify-content:flex-end}.toast-undo-btn{background:none;border:none;color:var(--cide-ele-brand-primary, #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:var(--cide-ele-brand-primary-light, #eff6ff)}.toast-progress{position:absolute;bottom:0;left:0;height:3px;background-color:var(--cide-ele-border-primary, #e5e7eb);animation:progressShrink linear forwards}.toast-notification.success .toast-progress{background-color:var(--cide-ele-success, #16a34a)}.toast-notification.error .toast-progress{background-color:var(--cide-ele-error, #dc2626)}.toast-notification.warning .toast-progress{background-color:var(--cide-ele-warning, #d97706)}.toast-notification.info .toast-progress{background-color:var(--cide-ele-info, #2563eb)}.toast-custom-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background-color:var(--cide-ele-border-primary, #e5e7eb);overflow:hidden}.toast-custom-progress-bar{height:100%;background-color:var(--cide-ele-info, #2563eb);transition:width .3s ease;border-radius:0 3px 0 0}.toast-notification.success .toast-custom-progress-bar{background-color:var(--cide-ele-success, #16a34a)}.toast-notification.error .toast-custom-progress-bar{background-color:var(--cide-ele-error, #dc2626)}.toast-notification.warning .toast-custom-progress-bar{background-color:var(--cide-ele-warning, #d97706)}.toast-notification.info .toast-custom-progress-bar{background-color:var(--cide-ele-info, #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 var(--cide-ele-brand-primary, #2563eb);outline-offset:2px}\n"] }]
|
|
9275
9356
|
}] });
|
|
9276
9357
|
|
|
9277
9358
|
class CideEleGlobalNotificationsComponent {
|
|
@@ -9590,16 +9671,19 @@ class CideEleDataGridComponent {
|
|
|
9590
9671
|
}
|
|
9591
9672
|
/**
|
|
9592
9673
|
* Initialize default grouping for columns with groupable: true
|
|
9674
|
+
* Only groups visible columns (not hidden ones)
|
|
9593
9675
|
*/
|
|
9594
9676
|
initializeDefaultGrouping() {
|
|
9595
9677
|
// Only initialize if groupedColumns is empty (not already set)
|
|
9596
9678
|
if (this.groupedColumns().length === 0) {
|
|
9597
9679
|
const columns = this.mergedConfig().columns || [];
|
|
9680
|
+
const hidden = this.hiddenColumns();
|
|
9681
|
+
// Only auto-group visible columns that are groupable (exclude hidden columns)
|
|
9598
9682
|
const groupableColumns = columns
|
|
9599
|
-
.filter(col => col.groupable === true)
|
|
9683
|
+
.filter(col => col.groupable === true && !col.hidden && !hidden.includes(col.key))
|
|
9600
9684
|
.map(col => col.key);
|
|
9601
9685
|
if (groupableColumns.length > 0) {
|
|
9602
|
-
console.log('🔍 Auto-applying grouping for columns:', groupableColumns);
|
|
9686
|
+
console.log('🔍 Auto-applying grouping for visible columns:', groupableColumns);
|
|
9603
9687
|
this.groupedColumns.set(groupableColumns);
|
|
9604
9688
|
// Reset auto-expand flag so groups will be expanded on first render
|
|
9605
9689
|
this.hasAutoExpandedGroups = false;
|
|
@@ -12091,7 +12175,7 @@ class CideEleDataGridComponent {
|
|
|
12091
12175
|
return String(value);
|
|
12092
12176
|
}
|
|
12093
12177
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDataGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12094
|
-
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-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 with Dropdown -->\n <div class=\"tw-relative\">\n <button type=\"button\"\n class=\"filter-dropdown-trigger 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-blue-600 hover:tw-bg-blue-50 tw-transition-all tw-duration-200 tw-relative\"\n [class.tw-text-blue-600]=\"columnFilters().length > 0\"\n [class.tw-bg-blue-50]=\"columnFilters().length > 0\"\n title=\"Filter\"\n (click)=\"toggleFilterDropdown()\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\n @if (columnFilters().length > 0) {\n <span class=\"tw-absolute -tw-top-1 -tw-right-1 tw-inline-flex tw-items-center tw-justify-center tw-w-4 tw-h-4 tw-text-xs tw-font-semibold tw-text-white tw-bg-blue-600 tw-rounded-full tw-border tw-border-white\">\n {{ columnFilters().length }}\n </span>\n }\n </button>\n \n <!-- Filter Dropdown Menu -->\n @if (showFilterDropdown()) {\n <div class=\"filter-dropdown tw-absolute tw-left-0 tw-mt-2 tw-w-80 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Active Filters</span>\n @if (columnFilters().length > 0) {\n <button type=\"button\" \n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-800 tw-font-medium\"\n (click)=\"clearAllFilters()\">\n Clear All\n </button>\n }\n </div>\n @if (columnFilters().length === 0) {\n <div class=\"tw-px-4 tw-py-6 tw-text-center\">\n <cide-ele-icon class=\"tw-w-12 tw-h-12 tw-text-gray-300 tw-mx-auto tw-mb-2\">filter_list</cide-ele-icon>\n <p class=\"tw-text-sm tw-text-gray-500\">No filters applied</p>\n <p class=\"tw-text-xs tw-text-gray-400 tw-mt-1\">Use column filters to narrow down your results</p>\n </div>\n } @else {\n <div class=\"tw-max-h-96 tw-overflow-y-auto\">\n @for (filter of columnFilters(); track filter.columnKey) {\n <div class=\"tw-px-4 tw-py-3 hover:tw-bg-gray-50 tw-border-b tw-border-gray-100 last:tw-border-b-0\">\n <div class=\"tw-flex tw-items-start tw-justify-between tw-gap-2\">\n <div class=\"tw-flex-1 tw-min-w-0\">\n <div class=\"tw-text-xs tw-font-semibold tw-text-gray-600 tw-mb-1\">{{ getColumnHeader(filter.columnKey) }}</div>\n <div class=\"tw-text-sm tw-text-gray-700 tw-flex tw-items-center tw-gap-1\">\n @if (filter.operator === 'in' && isArray(filter.value)) {\n <span class=\"tw-inline-flex tw-items-center tw-gap-1\">\n <cide-ele-icon class=\"tw-w-3 tw-h-3\">check_circle</cide-ele-icon>\n {{ getArrayLength(filter.value) }} selected\n </span>\n } @else {\n <span>{{ $any(filter.value) }}</span>\n }\n </div>\n </div>\n <button type=\"button\"\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-rounded tw-text-gray-400 hover:tw-text-red-600 hover:tw-bg-red-50 tw-transition-colors\"\n (click)=\"removeFilter(filter.columnKey)\"\n title=\"Remove filter\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n \n <!-- Download/Export Button with Dropdown -->\n <div class=\"tw-relative\">\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=\"Export\"\n (click)=\"toggleExportMenu($event)\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\n </button>\n \n <!-- Export Dropdown Menu - Improved Design -->\n @if (showExportMenu()) {\n <div class=\"tw-absolute tw-right-0 tw-mt-2 tw-w-56 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Export Options</span>\n </div>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-green-50 hover:tw-text-green-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('csv')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-green-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-green-600\">description</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">CSV</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Comma separated values</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-blue-50 hover:tw-text-blue-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('excel')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">table_chart</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">Excel</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Microsoft Excel format</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-red-50 hover:tw-text-red-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('pdf')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-red-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-red-600\">picture_as_pdf</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">PDF</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Portable document format</div>\n </div>\n </button>\n </div>\n </div>\n }\n </div>\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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\"\n [title]=\"column.header\">\n <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-truncate tw-flex-1\">\n <span class=\"tw-truncate\">{{ column.header }}</span>\n @if (getColumnSortDirection(column.key) === 'asc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Ascending\">arrow_upward</cide-ele-icon>\n }\n @if (getColumnSortDirection(column.key) === 'desc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Descending\">arrow_downward</cide-ele-icon>\n }\n </div>\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-[9999] 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 @let sortDirection = getColumnSortDirection(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'asc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'asc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'asc'\"\n (click)=\"onColumnSort(column, 'asc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'asc'\">arrow_upward</cide-ele-icon>\n Ascending\n </div>\n @if (sortDirection === 'asc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\n </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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'desc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'desc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'desc'\"\n (click)=\"onColumnSort(column, 'desc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\"\n [class.tw-text-blue-600]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'desc'\">arrow_downward</cide-ele-icon>\n Descending\n </div>\n @if (sortDirection === 'desc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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 @let isGrouped = groupedColumns().includes(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"isGrouped\"\n [class.tw-bg-blue-50]=\"isGrouped\"\n [class.tw-text-gray-700]=\"!isGrouped\"\n [class.hover:tw-bg-gray-50]=\"!isGrouped\"\n (click)=\"onColumnGroupBy(column)\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"isGrouped\"\n [class.tw-text-gray-400]=\"!isGrouped\">group_work</cide-ele-icon>\n Group By Column\n </div>\n @if (isGrouped) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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-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 visibleColumns(); 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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\">\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 @if (isGroupHeader(item)) {\n <!-- Group Header Row -->\n @let groupKey = getGroupKey(item);\n @let groupLevel = getGroupLevel(item);\n <tr class=\"data-grid-group-row tw-border-b-2 tw-border-gray-300 tw-font-semibold\"\n [class.tw-bg-gray-50]=\"groupLevel === 0\"\n [class.tw-bg-gray-100]=\"groupLevel === 1\"\n [class.tw-bg-gray-200]=\"groupLevel > 1\"\n (click)=\"toggleGroupExpansion(groupKey)\">\n <td class=\"tw-px-3 tw-py-1 tw-cursor-pointer tw-transition-colors\"\n [attr.colspan]=\"visibleColumns().length\">\n <div class=\"tw-flex tw-items-center tw-gap-2\" [style.padding-left.px]=\"groupLevel * 24\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-600 tw-cursor-pointer\">\n {{ isGroupExpanded(groupKey) ? 'expand_less' : 'expand_more' }}\n </cide-ele-icon>\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">group_work</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-900\">\n {{ getGroupColumnHeader(item) }}: {{ getGroupValueDisplay(item) }}\n </span>\n <span class=\"tw-text-xs tw-text-gray-500 tw-ml-2 tw-bg-gray-200 tw-px-2 tw-py-0.5 tw-rounded-full\">\n {{ getGroupRowCount(item) }}\n </span>\n </div>\n </td>\n </tr>\n } @else {\n <!-- Regular Data Row -->\n <tr class=\"tw-group data-grid-row 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 visibleColumns(); 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.width]=\"getColumnWidthStyle(column.width)\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width) || (getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : null)\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : (getColumnWidthStyle(column.width) ? null : '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 \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"visibleColumns().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\n <!-- Manage Columns Modal -->\n @if (showManageColumnsModal()) {\n <div class=\"tw-fixed tw-inset-0 tw-z-50 tw-overflow-y-auto\" \n (click)=\"closeManageColumnsModal()\"\n style=\"background-color: rgba(0, 0, 0, 0.5);\">\n <div class=\"tw-flex tw-items-center tw-justify-center tw-min-h-screen tw-p-4\">\n <div class=\"tw-bg-white tw-rounded-lg tw-shadow-xl tw-max-w-md tw-w-full tw-max-h-[80vh] tw-overflow-hidden tw-flex tw-flex-col\"\n (click)=\"$event.stopPropagation()\">\n <!-- Modal Header -->\n <div class=\"tw-px-6 tw-py-4 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-900\">Manage Columns</h3>\n <button type=\"button\"\n class=\"tw-text-gray-400 hover:tw-text-gray-600 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n <cide-ele-icon class=\"tw-w-5 tw-h-5\">close</cide-ele-icon>\n </button>\n </div>\n \n <!-- Modal Body -->\n <div class=\"tw-px-6 tw-py-4 tw-overflow-y-auto tw-flex-1\">\n <p class=\"tw-text-sm tw-text-gray-600 tw-mb-4\">Select columns to show or hide:</p>\n <div class=\"tw-space-y-2\">\n @for (column of mergedConfig().columns; track column.key) {\n <label class=\"tw-flex tw-items-center tw-px-3 tw-py-2 tw-rounded-md hover:tw-bg-gray-50 tw-cursor-pointer tw-transition-colors\">\n <input type=\"checkbox\"\n class=\"tw-mr-3 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\n [checked]=\"!isColumnHidden(column.key)\"\n (change)=\"toggleColumnVisibility(column.key); $event.stopPropagation()\">\n <span class=\"tw-text-sm tw-text-gray-700 tw-flex-1\">{{ column.header }}</span>\n @if (isColumnHidden(column.key)) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility_off</cide-ele-icon>\n } @else {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility</cide-ele-icon>\n }\n </label>\n }\n </div>\n </div>\n \n <!-- Modal Footer -->\n <div class=\"tw-px-6 tw-py-4 tw-border-t tw-border-gray-200 tw-flex tw-items-center tw-justify-end tw-gap-3\">\n <button type=\"button\"\n class=\"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 tw-rounded-md hover:tw-bg-gray-50 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n Close\n </button>\n <button type=\"button\"\n class=\"tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-white tw-bg-blue-600 tw-border tw-border-transparent tw-rounded-md hover:tw-bg-blue-700 tw-transition-colors\"\n (click)=\"onColumnReset(); closeManageColumnsModal()\">\n Reset All\n </button>\n </div>\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:var(--cide-ele-text-primary);background-color:var(--cide-ele-grid-bg, var(--cide-ele-bg-primary, #ffffff));border-radius:0;overflow:hidden;box-shadow:var(--cide-ele-shadow-sm);-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:var(--cide-ele-scrollbar-thumb, #d1d5db) var(--cide-ele-scrollbar-track, #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:var(--cide-ele-scrollbar-track, #f9fafb)}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:var(--cide-ele-scrollbar-thumb, #d1d5db);border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af)}.data-grid-container table{min-height:300px;border-collapse:separate;border-spacing:0;width:100%;background-color:var(--cide-ele-grid-bg)}.data-grid-container thead{background-color:var(--cide-ele-grid-bg-header);border-bottom:2px solid var(--cide-ele-grid-border-header);position:relative;z-index:100}.data-grid-container thead th{background:transparent;color:var(--cide-ele-grid-text-header);font-weight:500;font-size:12px;text-transform:none;letter-spacing:-.01em;padding:4px 10px;border-bottom:1px solid var(--cide-ele-grid-border);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:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, var(--cide-ele-bg-hover)))}.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:var(--cide-ele-bg-tertiary)}.data-grid-container tbody{background-color:var(--cide-ele-grid-bg, #ffffff)}.data-grid-container tbody td{padding:6px 10px;border-bottom:1px solid var(--cide-ele-grid-border, #e5e7eb);color:var(--cide-ele-grid-text, #1f2937);font-size:13px;vertical-align:middle;line-height:1.5;transition:all .2s cubic-bezier(.4,0,.2,1);background-color:transparent}.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:var(--cide-ele-grid-bg, #ffffff);transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody tr td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container tbody tr:hover,.data-grid-container tbody tr.data-grid-row:hover,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid transparent}.data-grid-container tbody tr:hover td,.data-grid-container tbody tr.data-grid-row:hover td,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td{background-color:transparent!important;border-bottom-color:var(--cide-ele-grid-border);color:var(--cide-ele-grid-text)!important}.data-grid-container tbody tr:active{background-color:var(--cide-ele-bg-active);transform:scale(.999)}.data-grid-container tbody tr:active td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-striped);border-left:1px solid transparent}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover{background-color:var(--cide-ele-grid-bg-hover);border-left:2px solid var(--cide-ele-grid-border-hover)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover td{color:var(--cide-ele-grid-text)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n) td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row{background-color:var(--cide-ele-grid-bg-group)!important}.data-grid-container tbody tr.data-grid-group-row td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container tbody tr.data-grid-group-row:hover td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.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:2px solid var(--cide-ele-grid-border-header);background-color:var(--cide-ele-grid-bg-header)}.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);position:absolute!important;z-index:9999!important}.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}}.data-grid-container :host .tw-bg-white{background-color:#fff!important}.data-grid-container :host .tw-bg-gray-50{background-color:#fafafa!important}.data-grid-container :host .tw-bg-gray-100{background-color:#f3f4f6!important}.data-grid-container :host thead{background-color:var(--cide-ele-grid-bg-header, #fafafa)!important}.data-grid-container :host thead th{background-color:transparent!important;color:var(--cide-ele-grid-text-header, #6b7280)!important}.data-grid-container :host thead th:hover{background-color:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, #f3f4f6))!important}.data-grid-container :host .data-grid-group-row{background-color:#f3f4f6!important}.data-grid-container :host tbody{background-color:#fff!important}.data-grid-container :host tbody tr{background-color:#fff!important}.data-grid-container :host tbody tr td{background-color:transparent}.data-grid-container :host.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}.data-grid-container :host .tw-text-gray-900{color:#1f2937!important}.data-grid-container :host .tw-text-gray-600{color:#4b5563!important}.data-grid-container :host .tw-text-gray-500{color:#6b7280!important}.data-grid-container :host .tw-text-gray-400{color:#9ca3af!important}.data-grid-container :host .tw-text-gray-700{color:#374151!important}.data-grid-container :host .tw-border-gray-200,.data-grid-container :host .tw-divide-gray-200{border-color:#e5e7eb!important}.data-grid-container :host .tw-border-gray-300{border-color:#d1d5db!important}.data-grid-container :host tbody tr:hover,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container :host tbody tr:hover td,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr:hover .tw-text-gray-900,.data-grid-container :host tbody tr:hover .tw-text-gray-700,.data-grid-container :host tbody tr:hover .tw-text-gray-600,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50{background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td{background-color:transparent}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900{color:#1f2937!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600{color:#4b5563!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500{color:#6b7280!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400{color:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700{color:#374151!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200{border-color:#e5e7eb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300{border-color:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}.data-grid-container :host-context(.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark]) .tw-bg-white,.data-grid-container :host-context(body.dark-mode) .tw-bg-white,.data-grid-container :host-context(html.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-white{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-50,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-50{background-color:var(--cide-ele-grid-bg-header, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-100,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-100{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark]) .data-grid-group-row,.data-grid-container :host-context(body.dark-mode) .data-grid-group-row,.data-grid-container :host-context(html.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] body) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] html) .data-grid-group-row{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark]) tbody,.data-grid-container :host-context(body.dark-mode) tbody,.data-grid-container :host-context(html.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark] body) tbody,.data-grid-container :host-context([data-theme=dark] html) tbody{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark]) tbody tr,.data-grid-container :host-context(body.dark-mode) tbody tr,.data-grid-container :host-context(html.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark] body) tbody tr,.data-grid-container :host-context([data-theme=dark] html) tbody tr{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark]) tbody tr td,.data-grid-container :host-context(body.dark-mode) tbody tr td,.data-grid-container :host-context(html.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark] body) tbody tr td,.data-grid-container :host-context([data-theme=dark] html) tbody tr td{background-color:transparent}.data-grid-container :host-context(.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark]).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(body.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(html.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] body).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] html).tw-table-striped tbody tr:nth-child(2n){background-color:var(--cide-ele-grid-bg-striped, #111827)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-900{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-600{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-500,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-500,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-500{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-400,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-400,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-400{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-700{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-divide-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-divide-gray-200{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-300,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-300,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-300{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover,.data-grid-container :host-context(body.dark-mode) tbody tr:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context(html.dark-mode) tbody tr:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important;border-left:2px solid var(--cide-ele-grid-border-hover, #60a5fa)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #f9fafb)}.data-grid-container.drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed var(--cide-ele-brand-primary, #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:var(--cide-ele-grid-bg-hover, #f9fafb);box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-grid-text, #1f2937)}.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:var(--cide-ele-bg-tertiary);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:var(--cide-ele-border-primary)}.tree-level-0{border-bottom:2px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-grid-bg)}.tree-level-0:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-grid-bg-striped)}.tree-level-1:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-bg-secondary)}.tree-level-2:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-bg-primary)}.tree-level-deep:hover{background-color:var(--cide-ele-bg-hover)!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", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey", "treeView"], 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], cide-ele-button", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }] });
|
|
12178
|
+
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-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 with Dropdown -->\n <div class=\"tw-relative\">\n <button type=\"button\"\n class=\"filter-dropdown-trigger 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-blue-600 hover:tw-bg-blue-50 tw-transition-all tw-duration-200 tw-relative\"\n [class.tw-text-blue-600]=\"columnFilters().length > 0\"\n [class.tw-bg-blue-50]=\"columnFilters().length > 0\"\n title=\"Filter\"\n (click)=\"toggleFilterDropdown()\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\n @if (columnFilters().length > 0) {\n <span class=\"tw-absolute -tw-top-1 -tw-right-1 tw-inline-flex tw-items-center tw-justify-center tw-w-4 tw-h-4 tw-text-xs tw-font-semibold tw-text-white tw-bg-blue-600 tw-rounded-full tw-border tw-border-white\">\n {{ columnFilters().length }}\n </span>\n }\n </button>\n \n <!-- Filter Dropdown Menu -->\n @if (showFilterDropdown()) {\n <div class=\"filter-dropdown tw-absolute tw-left-0 tw-mt-2 tw-w-80 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Active Filters</span>\n @if (columnFilters().length > 0) {\n <button type=\"button\" \n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-800 tw-font-medium\"\n (click)=\"clearAllFilters()\">\n Clear All\n </button>\n }\n </div>\n @if (columnFilters().length === 0) {\n <div class=\"tw-px-4 tw-py-6 tw-text-center\">\n <cide-ele-icon class=\"tw-w-12 tw-h-12 tw-text-gray-300 tw-mx-auto tw-mb-2\">filter_list</cide-ele-icon>\n <p class=\"tw-text-sm tw-text-gray-500\">No filters applied</p>\n <p class=\"tw-text-xs tw-text-gray-400 tw-mt-1\">Use column filters to narrow down your results</p>\n </div>\n } @else {\n <div class=\"tw-max-h-96 tw-overflow-y-auto\">\n @for (filter of columnFilters(); track filter.columnKey) {\n <div class=\"tw-px-4 tw-py-3 hover:tw-bg-gray-50 tw-border-b tw-border-gray-100 last:tw-border-b-0\">\n <div class=\"tw-flex tw-items-start tw-justify-between tw-gap-2\">\n <div class=\"tw-flex-1 tw-min-w-0\">\n <div class=\"tw-text-xs tw-font-semibold tw-text-gray-600 tw-mb-1\">{{ getColumnHeader(filter.columnKey) }}</div>\n <div class=\"tw-text-sm tw-text-gray-700 tw-flex tw-items-center tw-gap-1\">\n @if (filter.operator === 'in' && isArray(filter.value)) {\n <span class=\"tw-inline-flex tw-items-center tw-gap-1\">\n <cide-ele-icon class=\"tw-w-3 tw-h-3\">check_circle</cide-ele-icon>\n {{ getArrayLength(filter.value) }} selected\n </span>\n } @else {\n <span>{{ $any(filter.value) }}</span>\n }\n </div>\n </div>\n <button type=\"button\"\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-rounded tw-text-gray-400 hover:tw-text-red-600 hover:tw-bg-red-50 tw-transition-colors\"\n (click)=\"removeFilter(filter.columnKey)\"\n title=\"Remove filter\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n \n <!-- Download/Export Button with Dropdown -->\n <div class=\"tw-relative\">\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=\"Export\"\n (click)=\"toggleExportMenu($event)\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\n </button>\n \n <!-- Export Dropdown Menu - Improved Design -->\n @if (showExportMenu()) {\n <div class=\"tw-absolute tw-right-0 tw-mt-2 tw-w-56 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Export Options</span>\n </div>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-green-50 hover:tw-text-green-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('csv')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-green-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-green-600\">description</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">CSV</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Comma separated values</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-blue-50 hover:tw-text-blue-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('excel')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">table_chart</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">Excel</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Microsoft Excel format</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-red-50 hover:tw-text-red-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('pdf')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-red-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-red-600\">picture_as_pdf</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">PDF</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Portable document format</div>\n </div>\n </button>\n </div>\n </div>\n }\n </div>\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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\"\n [title]=\"column.header\">\n <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-truncate tw-flex-1\">\n <span class=\"tw-truncate\">{{ column.header }}</span>\n @if (getColumnSortDirection(column.key) === 'asc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Ascending\">arrow_upward</cide-ele-icon>\n }\n @if (getColumnSortDirection(column.key) === 'desc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Descending\">arrow_downward</cide-ele-icon>\n }\n </div>\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-[9999] 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 @let sortDirection = getColumnSortDirection(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'asc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'asc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'asc'\"\n (click)=\"onColumnSort(column, 'asc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'asc'\">arrow_upward</cide-ele-icon>\n Ascending\n </div>\n @if (sortDirection === 'asc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\n </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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'desc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'desc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'desc'\"\n (click)=\"onColumnSort(column, 'desc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\"\n [class.tw-text-blue-600]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'desc'\">arrow_downward</cide-ele-icon>\n Descending\n </div>\n @if (sortDirection === 'desc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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 @let isGrouped = groupedColumns().includes(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"isGrouped\"\n [class.tw-bg-blue-50]=\"isGrouped\"\n [class.tw-text-gray-700]=\"!isGrouped\"\n [class.hover:tw-bg-gray-50]=\"!isGrouped\"\n (click)=\"onColumnGroupBy(column)\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"isGrouped\"\n [class.tw-text-gray-400]=\"!isGrouped\">group_work</cide-ele-icon>\n Group By Column\n </div>\n @if (isGrouped) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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-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 visibleColumns(); 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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\">\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 @if (isGroupHeader(item)) {\n <!-- Group Header Row -->\n @let groupKey = getGroupKey(item);\n @let groupLevel = getGroupLevel(item);\n <tr class=\"data-grid-group-row tw-border-b-2 tw-border-gray-300 tw-font-semibold\"\n [class.tw-bg-gray-50]=\"groupLevel === 0\"\n [class.tw-bg-gray-100]=\"groupLevel === 1\"\n [class.tw-bg-gray-200]=\"groupLevel > 1\"\n (click)=\"toggleGroupExpansion(groupKey)\">\n <td class=\"tw-px-3 tw-py-1 tw-cursor-pointer tw-transition-colors\"\n [attr.colspan]=\"visibleColumns().length\">\n <div class=\"tw-flex tw-items-center tw-gap-2\" [style.padding-left.px]=\"groupLevel * 24\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-600 tw-cursor-pointer\">\n {{ isGroupExpanded(groupKey) ? 'expand_more' : 'chevron_right' }}\n </cide-ele-icon>\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">group_work</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-900\">\n {{ getGroupColumnHeader(item) }}: {{ getGroupValueDisplay(item) }}\n </span>\n <span class=\"tw-text-xs tw-text-gray-500 tw-ml-2 tw-bg-gray-200 tw-px-2 tw-py-0.5 tw-rounded-full\">\n {{ getGroupRowCount(item) }}\n </span>\n </div>\n </td>\n </tr>\n } @else {\n <!-- Regular Data Row -->\n <tr class=\"tw-group data-grid-row 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 visibleColumns(); 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.width]=\"getColumnWidthStyle(column.width)\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width) || (getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : null)\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : (getColumnWidthStyle(column.width) ? null : '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 \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"visibleColumns().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\n <!-- Manage Columns Modal -->\n @if (showManageColumnsModal()) {\n <div class=\"tw-fixed tw-inset-0 tw-z-50 tw-overflow-y-auto\" \n (click)=\"closeManageColumnsModal()\"\n style=\"background-color: rgba(0, 0, 0, 0.5);\">\n <div class=\"tw-flex tw-items-center tw-justify-center tw-min-h-screen tw-p-4\">\n <div class=\"tw-bg-white tw-rounded-lg tw-shadow-xl tw-max-w-md tw-w-full tw-max-h-[80vh] tw-overflow-hidden tw-flex tw-flex-col\"\n (click)=\"$event.stopPropagation()\">\n <!-- Modal Header -->\n <div class=\"tw-px-6 tw-py-4 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-900\">Manage Columns</h3>\n <button type=\"button\"\n class=\"tw-text-gray-400 hover:tw-text-gray-600 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n <cide-ele-icon class=\"tw-w-5 tw-h-5\">close</cide-ele-icon>\n </button>\n </div>\n \n <!-- Modal Body -->\n <div class=\"tw-px-6 tw-py-4 tw-overflow-y-auto tw-flex-1\">\n <p class=\"tw-text-sm tw-text-gray-600 tw-mb-4\">Select columns to show or hide:</p>\n <div class=\"tw-space-y-2\">\n @for (column of mergedConfig().columns; track column.key) {\n <label class=\"tw-flex tw-items-center tw-px-3 tw-py-2 tw-rounded-md hover:tw-bg-gray-50 tw-cursor-pointer tw-transition-colors\">\n <input type=\"checkbox\"\n class=\"tw-mr-3 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\n [checked]=\"!isColumnHidden(column.key)\"\n (change)=\"toggleColumnVisibility(column.key); $event.stopPropagation()\">\n <span class=\"tw-text-sm tw-text-gray-700 tw-flex-1\">{{ column.header }}</span>\n @if (isColumnHidden(column.key)) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility_off</cide-ele-icon>\n } @else {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility</cide-ele-icon>\n }\n </label>\n }\n </div>\n </div>\n \n <!-- Modal Footer -->\n <div class=\"tw-px-6 tw-py-4 tw-border-t tw-border-gray-200 tw-flex tw-items-center tw-justify-end tw-gap-3\">\n <button type=\"button\"\n class=\"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 tw-rounded-md hover:tw-bg-gray-50 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n Close\n </button>\n <button type=\"button\"\n class=\"tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-white tw-bg-blue-600 tw-border tw-border-transparent tw-rounded-md hover:tw-bg-blue-700 tw-transition-colors\"\n (click)=\"onColumnReset(); closeManageColumnsModal()\">\n Reset All\n </button>\n </div>\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:var(--cide-ele-text-primary);background-color:var(--cide-ele-grid-bg, var(--cide-ele-bg-primary, #ffffff));border-radius:0;overflow:hidden;box-shadow:var(--cide-ele-shadow-sm);-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:var(--cide-ele-scrollbar-thumb, #d1d5db) var(--cide-ele-scrollbar-track, #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:var(--cide-ele-scrollbar-track, #f9fafb)}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:var(--cide-ele-scrollbar-thumb, #d1d5db);border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af)}.data-grid-container table{min-height:300px;border-collapse:separate;border-spacing:0;width:100%;background-color:var(--cide-ele-grid-bg)}.data-grid-container thead{background-color:var(--cide-ele-grid-bg-header);border-bottom:2px solid var(--cide-ele-grid-border-header);position:relative;z-index:100}.data-grid-container thead th{background:transparent;color:var(--cide-ele-grid-text-header);font-weight:500;font-size:12px;text-transform:none;letter-spacing:-.01em;padding:4px 10px;border-bottom:1px solid var(--cide-ele-grid-border);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:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, var(--cide-ele-bg-hover)))}.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:var(--cide-ele-bg-tertiary)}.data-grid-container tbody{background-color:var(--cide-ele-grid-bg, #ffffff)}.data-grid-container tbody td{padding:6px 10px;border-bottom:1px solid var(--cide-ele-grid-border, #e5e7eb);color:var(--cide-ele-grid-text, #1f2937);font-size:13px;vertical-align:middle;line-height:1.5;transition:all .2s cubic-bezier(.4,0,.2,1);background-color:transparent}.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:var(--cide-ele-grid-bg, #ffffff);transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody tr td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container tbody tr:hover,.data-grid-container tbody tr.data-grid-row:hover,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid transparent}.data-grid-container tbody tr:hover td,.data-grid-container tbody tr.data-grid-row:hover td,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td{background-color:transparent!important;border-bottom-color:var(--cide-ele-grid-border);color:var(--cide-ele-grid-text)!important}.data-grid-container tbody tr:active{background-color:var(--cide-ele-bg-active);transform:scale(.999)}.data-grid-container tbody tr:active td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-striped);border-left:1px solid transparent}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover{background-color:var(--cide-ele-grid-bg-hover);border-left:2px solid var(--cide-ele-grid-border-hover)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover td{color:var(--cide-ele-grid-text)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n) td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row{background-color:var(--cide-ele-grid-bg-group)!important}.data-grid-container tbody tr.data-grid-group-row td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container tbody tr.data-grid-group-row:hover td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.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:2px solid var(--cide-ele-grid-border-header);background-color:var(--cide-ele-grid-bg-header)}.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);position:absolute!important;z-index:9999!important}.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}}.data-grid-container :host .tw-bg-white{background-color:#fff!important}.data-grid-container :host .tw-bg-gray-50{background-color:#fafafa!important}.data-grid-container :host .tw-bg-gray-100{background-color:#f3f4f6!important}.data-grid-container :host thead{background-color:var(--cide-ele-grid-bg-header, #fafafa)!important}.data-grid-container :host thead th{background-color:transparent!important;color:var(--cide-ele-grid-text-header, #6b7280)!important}.data-grid-container :host thead th:hover{background-color:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, #f3f4f6))!important}.data-grid-container :host .data-grid-group-row{background-color:#f3f4f6!important}.data-grid-container :host tbody{background-color:#fff!important}.data-grid-container :host tbody tr{background-color:#fff!important}.data-grid-container :host tbody tr td{background-color:transparent}.data-grid-container :host.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}.data-grid-container :host .tw-text-gray-900{color:#1f2937!important}.data-grid-container :host .tw-text-gray-600{color:#4b5563!important}.data-grid-container :host .tw-text-gray-500{color:#6b7280!important}.data-grid-container :host .tw-text-gray-400{color:#9ca3af!important}.data-grid-container :host .tw-text-gray-700{color:#374151!important}.data-grid-container :host .tw-border-gray-200,.data-grid-container :host .tw-divide-gray-200{border-color:#e5e7eb!important}.data-grid-container :host .tw-border-gray-300{border-color:#d1d5db!important}.data-grid-container :host tbody tr:hover,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container :host tbody tr:hover td,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr:hover .tw-text-gray-900,.data-grid-container :host tbody tr:hover .tw-text-gray-700,.data-grid-container :host tbody tr:hover .tw-text-gray-600,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50{background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td{background-color:transparent}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900{color:#1f2937!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600{color:#4b5563!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500{color:#6b7280!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400{color:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700{color:#374151!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200{border-color:#e5e7eb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300{border-color:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}.data-grid-container :host-context(.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark]) .tw-bg-white,.data-grid-container :host-context(body.dark-mode) .tw-bg-white,.data-grid-container :host-context(html.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-white{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-50,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-50{background-color:var(--cide-ele-grid-bg-header, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-100,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-100{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark]) .data-grid-group-row,.data-grid-container :host-context(body.dark-mode) .data-grid-group-row,.data-grid-container :host-context(html.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] body) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] html) .data-grid-group-row{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark]) tbody,.data-grid-container :host-context(body.dark-mode) tbody,.data-grid-container :host-context(html.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark] body) tbody,.data-grid-container :host-context([data-theme=dark] html) tbody{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark]) tbody tr,.data-grid-container :host-context(body.dark-mode) tbody tr,.data-grid-container :host-context(html.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark] body) tbody tr,.data-grid-container :host-context([data-theme=dark] html) tbody tr{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark]) tbody tr td,.data-grid-container :host-context(body.dark-mode) tbody tr td,.data-grid-container :host-context(html.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark] body) tbody tr td,.data-grid-container :host-context([data-theme=dark] html) tbody tr td{background-color:transparent}.data-grid-container :host-context(.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark]).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(body.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(html.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] body).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] html).tw-table-striped tbody tr:nth-child(2n){background-color:var(--cide-ele-grid-bg-striped, #111827)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-900{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-600{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-500,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-500,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-500{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-400,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-400,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-400{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-700{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-divide-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-divide-gray-200{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-300,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-300,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-300{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover,.data-grid-container :host-context(body.dark-mode) tbody tr:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context(html.dark-mode) tbody tr:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important;border-left:2px solid var(--cide-ele-grid-border-hover, #60a5fa)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #f9fafb)}.data-grid-container.drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed var(--cide-ele-brand-primary, #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:var(--cide-ele-grid-bg-hover, #f9fafb);box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-grid-text, #1f2937)}.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:var(--cide-ele-bg-tertiary);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:var(--cide-ele-border-primary)}.tree-level-0{border-bottom:2px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-grid-bg)}.tree-level-0:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-grid-bg-striped)}.tree-level-1:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-bg-secondary)}.tree-level-2:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-bg-primary)}.tree-level-deep:hover{background-color:var(--cide-ele-bg-hover)!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", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey", "treeView"], 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], cide-ele-button", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }] });
|
|
12095
12179
|
}
|
|
12096
12180
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleDataGridComponent, decorators: [{
|
|
12097
12181
|
type: Component,
|
|
@@ -12103,7 +12187,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
12103
12187
|
CideSelectComponent,
|
|
12104
12188
|
CideIconComponent,
|
|
12105
12189
|
CideEleButtonComponent
|
|
12106
|
-
], template: " <!-- Data Grid Component -->\n <div class=\"data-grid-container tw-bg-white tw-shadow 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 with Dropdown -->\n <div class=\"tw-relative\">\n <button type=\"button\"\n class=\"filter-dropdown-trigger 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-blue-600 hover:tw-bg-blue-50 tw-transition-all tw-duration-200 tw-relative\"\n [class.tw-text-blue-600]=\"columnFilters().length > 0\"\n [class.tw-bg-blue-50]=\"columnFilters().length > 0\"\n title=\"Filter\"\n (click)=\"toggleFilterDropdown()\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\n @if (columnFilters().length > 0) {\n <span class=\"tw-absolute -tw-top-1 -tw-right-1 tw-inline-flex tw-items-center tw-justify-center tw-w-4 tw-h-4 tw-text-xs tw-font-semibold tw-text-white tw-bg-blue-600 tw-rounded-full tw-border tw-border-white\">\n {{ columnFilters().length }}\n </span>\n }\n </button>\n \n <!-- Filter Dropdown Menu -->\n @if (showFilterDropdown()) {\n <div class=\"filter-dropdown tw-absolute tw-left-0 tw-mt-2 tw-w-80 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Active Filters</span>\n @if (columnFilters().length > 0) {\n <button type=\"button\" \n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-800 tw-font-medium\"\n (click)=\"clearAllFilters()\">\n Clear All\n </button>\n }\n </div>\n @if (columnFilters().length === 0) {\n <div class=\"tw-px-4 tw-py-6 tw-text-center\">\n <cide-ele-icon class=\"tw-w-12 tw-h-12 tw-text-gray-300 tw-mx-auto tw-mb-2\">filter_list</cide-ele-icon>\n <p class=\"tw-text-sm tw-text-gray-500\">No filters applied</p>\n <p class=\"tw-text-xs tw-text-gray-400 tw-mt-1\">Use column filters to narrow down your results</p>\n </div>\n } @else {\n <div class=\"tw-max-h-96 tw-overflow-y-auto\">\n @for (filter of columnFilters(); track filter.columnKey) {\n <div class=\"tw-px-4 tw-py-3 hover:tw-bg-gray-50 tw-border-b tw-border-gray-100 last:tw-border-b-0\">\n <div class=\"tw-flex tw-items-start tw-justify-between tw-gap-2\">\n <div class=\"tw-flex-1 tw-min-w-0\">\n <div class=\"tw-text-xs tw-font-semibold tw-text-gray-600 tw-mb-1\">{{ getColumnHeader(filter.columnKey) }}</div>\n <div class=\"tw-text-sm tw-text-gray-700 tw-flex tw-items-center tw-gap-1\">\n @if (filter.operator === 'in' && isArray(filter.value)) {\n <span class=\"tw-inline-flex tw-items-center tw-gap-1\">\n <cide-ele-icon class=\"tw-w-3 tw-h-3\">check_circle</cide-ele-icon>\n {{ getArrayLength(filter.value) }} selected\n </span>\n } @else {\n <span>{{ $any(filter.value) }}</span>\n }\n </div>\n </div>\n <button type=\"button\"\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-rounded tw-text-gray-400 hover:tw-text-red-600 hover:tw-bg-red-50 tw-transition-colors\"\n (click)=\"removeFilter(filter.columnKey)\"\n title=\"Remove filter\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n \n <!-- Download/Export Button with Dropdown -->\n <div class=\"tw-relative\">\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=\"Export\"\n (click)=\"toggleExportMenu($event)\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\n </button>\n \n <!-- Export Dropdown Menu - Improved Design -->\n @if (showExportMenu()) {\n <div class=\"tw-absolute tw-right-0 tw-mt-2 tw-w-56 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Export Options</span>\n </div>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-green-50 hover:tw-text-green-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('csv')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-green-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-green-600\">description</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">CSV</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Comma separated values</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-blue-50 hover:tw-text-blue-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('excel')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">table_chart</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">Excel</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Microsoft Excel format</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-red-50 hover:tw-text-red-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('pdf')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-red-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-red-600\">picture_as_pdf</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">PDF</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Portable document format</div>\n </div>\n </button>\n </div>\n </div>\n }\n </div>\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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\"\n [title]=\"column.header\">\n <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-truncate tw-flex-1\">\n <span class=\"tw-truncate\">{{ column.header }}</span>\n @if (getColumnSortDirection(column.key) === 'asc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Ascending\">arrow_upward</cide-ele-icon>\n }\n @if (getColumnSortDirection(column.key) === 'desc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Descending\">arrow_downward</cide-ele-icon>\n }\n </div>\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-[9999] 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 @let sortDirection = getColumnSortDirection(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'asc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'asc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'asc'\"\n (click)=\"onColumnSort(column, 'asc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'asc'\">arrow_upward</cide-ele-icon>\n Ascending\n </div>\n @if (sortDirection === 'asc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\n </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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'desc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'desc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'desc'\"\n (click)=\"onColumnSort(column, 'desc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\"\n [class.tw-text-blue-600]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'desc'\">arrow_downward</cide-ele-icon>\n Descending\n </div>\n @if (sortDirection === 'desc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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 @let isGrouped = groupedColumns().includes(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"isGrouped\"\n [class.tw-bg-blue-50]=\"isGrouped\"\n [class.tw-text-gray-700]=\"!isGrouped\"\n [class.hover:tw-bg-gray-50]=\"!isGrouped\"\n (click)=\"onColumnGroupBy(column)\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"isGrouped\"\n [class.tw-text-gray-400]=\"!isGrouped\">group_work</cide-ele-icon>\n Group By Column\n </div>\n @if (isGrouped) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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-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 visibleColumns(); 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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\">\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 @if (isGroupHeader(item)) {\n <!-- Group Header Row -->\n @let groupKey = getGroupKey(item);\n @let groupLevel = getGroupLevel(item);\n <tr class=\"data-grid-group-row tw-border-b-2 tw-border-gray-300 tw-font-semibold\"\n [class.tw-bg-gray-50]=\"groupLevel === 0\"\n [class.tw-bg-gray-100]=\"groupLevel === 1\"\n [class.tw-bg-gray-200]=\"groupLevel > 1\"\n (click)=\"toggleGroupExpansion(groupKey)\">\n <td class=\"tw-px-3 tw-py-1 tw-cursor-pointer tw-transition-colors\"\n [attr.colspan]=\"visibleColumns().length\">\n <div class=\"tw-flex tw-items-center tw-gap-2\" [style.padding-left.px]=\"groupLevel * 24\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-600 tw-cursor-pointer\">\n {{ isGroupExpanded(groupKey) ? 'expand_less' : 'expand_more' }}\n </cide-ele-icon>\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">group_work</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-900\">\n {{ getGroupColumnHeader(item) }}: {{ getGroupValueDisplay(item) }}\n </span>\n <span class=\"tw-text-xs tw-text-gray-500 tw-ml-2 tw-bg-gray-200 tw-px-2 tw-py-0.5 tw-rounded-full\">\n {{ getGroupRowCount(item) }}\n </span>\n </div>\n </td>\n </tr>\n } @else {\n <!-- Regular Data Row -->\n <tr class=\"tw-group data-grid-row 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 visibleColumns(); 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.width]=\"getColumnWidthStyle(column.width)\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width) || (getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : null)\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : (getColumnWidthStyle(column.width) ? null : '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 \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"visibleColumns().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\n <!-- Manage Columns Modal -->\n @if (showManageColumnsModal()) {\n <div class=\"tw-fixed tw-inset-0 tw-z-50 tw-overflow-y-auto\" \n (click)=\"closeManageColumnsModal()\"\n style=\"background-color: rgba(0, 0, 0, 0.5);\">\n <div class=\"tw-flex tw-items-center tw-justify-center tw-min-h-screen tw-p-4\">\n <div class=\"tw-bg-white tw-rounded-lg tw-shadow-xl tw-max-w-md tw-w-full tw-max-h-[80vh] tw-overflow-hidden tw-flex tw-flex-col\"\n (click)=\"$event.stopPropagation()\">\n <!-- Modal Header -->\n <div class=\"tw-px-6 tw-py-4 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-900\">Manage Columns</h3>\n <button type=\"button\"\n class=\"tw-text-gray-400 hover:tw-text-gray-600 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n <cide-ele-icon class=\"tw-w-5 tw-h-5\">close</cide-ele-icon>\n </button>\n </div>\n \n <!-- Modal Body -->\n <div class=\"tw-px-6 tw-py-4 tw-overflow-y-auto tw-flex-1\">\n <p class=\"tw-text-sm tw-text-gray-600 tw-mb-4\">Select columns to show or hide:</p>\n <div class=\"tw-space-y-2\">\n @for (column of mergedConfig().columns; track column.key) {\n <label class=\"tw-flex tw-items-center tw-px-3 tw-py-2 tw-rounded-md hover:tw-bg-gray-50 tw-cursor-pointer tw-transition-colors\">\n <input type=\"checkbox\"\n class=\"tw-mr-3 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\n [checked]=\"!isColumnHidden(column.key)\"\n (change)=\"toggleColumnVisibility(column.key); $event.stopPropagation()\">\n <span class=\"tw-text-sm tw-text-gray-700 tw-flex-1\">{{ column.header }}</span>\n @if (isColumnHidden(column.key)) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility_off</cide-ele-icon>\n } @else {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility</cide-ele-icon>\n }\n </label>\n }\n </div>\n </div>\n \n <!-- Modal Footer -->\n <div class=\"tw-px-6 tw-py-4 tw-border-t tw-border-gray-200 tw-flex tw-items-center tw-justify-end tw-gap-3\">\n <button type=\"button\"\n class=\"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 tw-rounded-md hover:tw-bg-gray-50 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n Close\n </button>\n <button type=\"button\"\n class=\"tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-white tw-bg-blue-600 tw-border tw-border-transparent tw-rounded-md hover:tw-bg-blue-700 tw-transition-colors\"\n (click)=\"onColumnReset(); closeManageColumnsModal()\">\n Reset All\n </button>\n </div>\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:var(--cide-ele-text-primary);background-color:var(--cide-ele-grid-bg, var(--cide-ele-bg-primary, #ffffff));border-radius:0;overflow:hidden;box-shadow:var(--cide-ele-shadow-sm);-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:var(--cide-ele-scrollbar-thumb, #d1d5db) var(--cide-ele-scrollbar-track, #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:var(--cide-ele-scrollbar-track, #f9fafb)}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:var(--cide-ele-scrollbar-thumb, #d1d5db);border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af)}.data-grid-container table{min-height:300px;border-collapse:separate;border-spacing:0;width:100%;background-color:var(--cide-ele-grid-bg)}.data-grid-container thead{background-color:var(--cide-ele-grid-bg-header);border-bottom:2px solid var(--cide-ele-grid-border-header);position:relative;z-index:100}.data-grid-container thead th{background:transparent;color:var(--cide-ele-grid-text-header);font-weight:500;font-size:12px;text-transform:none;letter-spacing:-.01em;padding:4px 10px;border-bottom:1px solid var(--cide-ele-grid-border);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:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, var(--cide-ele-bg-hover)))}.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:var(--cide-ele-bg-tertiary)}.data-grid-container tbody{background-color:var(--cide-ele-grid-bg, #ffffff)}.data-grid-container tbody td{padding:6px 10px;border-bottom:1px solid var(--cide-ele-grid-border, #e5e7eb);color:var(--cide-ele-grid-text, #1f2937);font-size:13px;vertical-align:middle;line-height:1.5;transition:all .2s cubic-bezier(.4,0,.2,1);background-color:transparent}.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:var(--cide-ele-grid-bg, #ffffff);transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody tr td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container tbody tr:hover,.data-grid-container tbody tr.data-grid-row:hover,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid transparent}.data-grid-container tbody tr:hover td,.data-grid-container tbody tr.data-grid-row:hover td,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td{background-color:transparent!important;border-bottom-color:var(--cide-ele-grid-border);color:var(--cide-ele-grid-text)!important}.data-grid-container tbody tr:active{background-color:var(--cide-ele-bg-active);transform:scale(.999)}.data-grid-container tbody tr:active td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-striped);border-left:1px solid transparent}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover{background-color:var(--cide-ele-grid-bg-hover);border-left:2px solid var(--cide-ele-grid-border-hover)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover td{color:var(--cide-ele-grid-text)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n) td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row{background-color:var(--cide-ele-grid-bg-group)!important}.data-grid-container tbody tr.data-grid-group-row td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container tbody tr.data-grid-group-row:hover td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.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:2px solid var(--cide-ele-grid-border-header);background-color:var(--cide-ele-grid-bg-header)}.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);position:absolute!important;z-index:9999!important}.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}}.data-grid-container :host .tw-bg-white{background-color:#fff!important}.data-grid-container :host .tw-bg-gray-50{background-color:#fafafa!important}.data-grid-container :host .tw-bg-gray-100{background-color:#f3f4f6!important}.data-grid-container :host thead{background-color:var(--cide-ele-grid-bg-header, #fafafa)!important}.data-grid-container :host thead th{background-color:transparent!important;color:var(--cide-ele-grid-text-header, #6b7280)!important}.data-grid-container :host thead th:hover{background-color:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, #f3f4f6))!important}.data-grid-container :host .data-grid-group-row{background-color:#f3f4f6!important}.data-grid-container :host tbody{background-color:#fff!important}.data-grid-container :host tbody tr{background-color:#fff!important}.data-grid-container :host tbody tr td{background-color:transparent}.data-grid-container :host.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}.data-grid-container :host .tw-text-gray-900{color:#1f2937!important}.data-grid-container :host .tw-text-gray-600{color:#4b5563!important}.data-grid-container :host .tw-text-gray-500{color:#6b7280!important}.data-grid-container :host .tw-text-gray-400{color:#9ca3af!important}.data-grid-container :host .tw-text-gray-700{color:#374151!important}.data-grid-container :host .tw-border-gray-200,.data-grid-container :host .tw-divide-gray-200{border-color:#e5e7eb!important}.data-grid-container :host .tw-border-gray-300{border-color:#d1d5db!important}.data-grid-container :host tbody tr:hover,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container :host tbody tr:hover td,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr:hover .tw-text-gray-900,.data-grid-container :host tbody tr:hover .tw-text-gray-700,.data-grid-container :host tbody tr:hover .tw-text-gray-600,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50{background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td{background-color:transparent}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900{color:#1f2937!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600{color:#4b5563!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500{color:#6b7280!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400{color:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700{color:#374151!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200{border-color:#e5e7eb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300{border-color:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}.data-grid-container :host-context(.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark]) .tw-bg-white,.data-grid-container :host-context(body.dark-mode) .tw-bg-white,.data-grid-container :host-context(html.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-white{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-50,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-50{background-color:var(--cide-ele-grid-bg-header, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-100,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-100{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark]) .data-grid-group-row,.data-grid-container :host-context(body.dark-mode) .data-grid-group-row,.data-grid-container :host-context(html.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] body) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] html) .data-grid-group-row{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark]) tbody,.data-grid-container :host-context(body.dark-mode) tbody,.data-grid-container :host-context(html.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark] body) tbody,.data-grid-container :host-context([data-theme=dark] html) tbody{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark]) tbody tr,.data-grid-container :host-context(body.dark-mode) tbody tr,.data-grid-container :host-context(html.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark] body) tbody tr,.data-grid-container :host-context([data-theme=dark] html) tbody tr{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark]) tbody tr td,.data-grid-container :host-context(body.dark-mode) tbody tr td,.data-grid-container :host-context(html.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark] body) tbody tr td,.data-grid-container :host-context([data-theme=dark] html) tbody tr td{background-color:transparent}.data-grid-container :host-context(.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark]).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(body.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(html.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] body).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] html).tw-table-striped tbody tr:nth-child(2n){background-color:var(--cide-ele-grid-bg-striped, #111827)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-900{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-600{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-500,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-500,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-500{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-400,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-400,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-400{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-700{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-divide-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-divide-gray-200{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-300,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-300,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-300{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover,.data-grid-container :host-context(body.dark-mode) tbody tr:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context(html.dark-mode) tbody tr:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important;border-left:2px solid var(--cide-ele-grid-border-hover, #60a5fa)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #f9fafb)}.data-grid-container.drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed var(--cide-ele-brand-primary, #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:var(--cide-ele-grid-bg-hover, #f9fafb);box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-grid-text, #1f2937)}.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:var(--cide-ele-bg-tertiary);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:var(--cide-ele-border-primary)}.tree-level-0{border-bottom:2px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-grid-bg)}.tree-level-0:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-grid-bg-striped)}.tree-level-1:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-bg-secondary)}.tree-level-2:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-bg-primary)}.tree-level-deep:hover{background-color:var(--cide-ele-bg-hover)!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"] }]
|
|
12190
|
+
], template: " <!-- Data Grid Component -->\n <div class=\"data-grid-container tw-bg-white tw-shadow 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 with Dropdown -->\n <div class=\"tw-relative\">\n <button type=\"button\"\n class=\"filter-dropdown-trigger 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-blue-600 hover:tw-bg-blue-50 tw-transition-all tw-duration-200 tw-relative\"\n [class.tw-text-blue-600]=\"columnFilters().length > 0\"\n [class.tw-bg-blue-50]=\"columnFilters().length > 0\"\n title=\"Filter\"\n (click)=\"toggleFilterDropdown()\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">filter_list</cide-ele-icon>\n @if (columnFilters().length > 0) {\n <span class=\"tw-absolute -tw-top-1 -tw-right-1 tw-inline-flex tw-items-center tw-justify-center tw-w-4 tw-h-4 tw-text-xs tw-font-semibold tw-text-white tw-bg-blue-600 tw-rounded-full tw-border tw-border-white\">\n {{ columnFilters().length }}\n </span>\n }\n </button>\n \n <!-- Filter Dropdown Menu -->\n @if (showFilterDropdown()) {\n <div class=\"filter-dropdown tw-absolute tw-left-0 tw-mt-2 tw-w-80 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Active Filters</span>\n @if (columnFilters().length > 0) {\n <button type=\"button\" \n class=\"tw-text-xs tw-text-blue-600 hover:tw-text-blue-800 tw-font-medium\"\n (click)=\"clearAllFilters()\">\n Clear All\n </button>\n }\n </div>\n @if (columnFilters().length === 0) {\n <div class=\"tw-px-4 tw-py-6 tw-text-center\">\n <cide-ele-icon class=\"tw-w-12 tw-h-12 tw-text-gray-300 tw-mx-auto tw-mb-2\">filter_list</cide-ele-icon>\n <p class=\"tw-text-sm tw-text-gray-500\">No filters applied</p>\n <p class=\"tw-text-xs tw-text-gray-400 tw-mt-1\">Use column filters to narrow down your results</p>\n </div>\n } @else {\n <div class=\"tw-max-h-96 tw-overflow-y-auto\">\n @for (filter of columnFilters(); track filter.columnKey) {\n <div class=\"tw-px-4 tw-py-3 hover:tw-bg-gray-50 tw-border-b tw-border-gray-100 last:tw-border-b-0\">\n <div class=\"tw-flex tw-items-start tw-justify-between tw-gap-2\">\n <div class=\"tw-flex-1 tw-min-w-0\">\n <div class=\"tw-text-xs tw-font-semibold tw-text-gray-600 tw-mb-1\">{{ getColumnHeader(filter.columnKey) }}</div>\n <div class=\"tw-text-sm tw-text-gray-700 tw-flex tw-items-center tw-gap-1\">\n @if (filter.operator === 'in' && isArray(filter.value)) {\n <span class=\"tw-inline-flex tw-items-center tw-gap-1\">\n <cide-ele-icon class=\"tw-w-3 tw-h-3\">check_circle</cide-ele-icon>\n {{ getArrayLength(filter.value) }} selected\n </span>\n } @else {\n <span>{{ $any(filter.value) }}</span>\n }\n </div>\n </div>\n <button type=\"button\"\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-rounded tw-text-gray-400 hover:tw-text-red-600 hover:tw-bg-red-50 tw-transition-colors\"\n (click)=\"removeFilter(filter.columnKey)\"\n title=\"Remove filter\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n \n <!-- Download/Export Button with Dropdown -->\n <div class=\"tw-relative\">\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=\"Export\"\n (click)=\"toggleExportMenu($event)\">\n <cide-ele-icon class=\"tw-w-[22px] tw-h-4\">file_download</cide-ele-icon>\n </button>\n \n <!-- Export Dropdown Menu - Improved Design -->\n @if (showExportMenu()) {\n <div class=\"tw-absolute tw-right-0 tw-mt-2 tw-w-56 tw-bg-white tw-rounded-lg tw-shadow-xl tw-border tw-border-gray-200 tw-z-50 tw-overflow-hidden\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"tw-py-1.5\">\n <div class=\"tw-px-3 tw-py-2 tw-bg-gray-50 tw-border-b tw-border-gray-200\">\n <span class=\"tw-text-xs tw-font-semibold tw-text-gray-500 tw-uppercase tw-tracking-wider\">Export Options</span>\n </div>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-green-50 hover:tw-text-green-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('csv')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-green-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-green-600\">description</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">CSV</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Comma separated values</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-blue-50 hover:tw-text-blue-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('excel')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">table_chart</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">Excel</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Microsoft Excel format</div>\n </div>\n </button>\n <button type=\"button\"\n class=\"tw-w-full tw-text-left tw-px-4 tw-py-3 tw-text-sm tw-text-gray-700 hover:tw-bg-red-50 hover:tw-text-red-700 tw-flex tw-items-center tw-transition-colors tw-duration-150\"\n (click)=\"exportData('pdf')\">\n <div class=\"tw-w-8 tw-h-8 tw-rounded tw-bg-red-100 tw-flex tw-items-center tw-justify-center tw-mr-3\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-red-600\">picture_as_pdf</cide-ele-icon>\n </div>\n <div class=\"tw-flex-1\">\n <div class=\"tw-font-medium\">PDF</div>\n <div class=\"tw-text-xs tw-text-gray-500\">Portable document format</div>\n </div>\n </button>\n </div>\n </div>\n }\n </div>\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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\"\n [title]=\"column.header\">\n <div class=\"tw-flex tw-items-center tw-justify-between tw-gap-1\">\n <div class=\"tw-flex tw-items-center tw-gap-1 tw-truncate tw-flex-1\">\n <span class=\"tw-truncate\">{{ column.header }}</span>\n @if (getColumnSortDirection(column.key) === 'asc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Ascending\">arrow_upward</cide-ele-icon>\n }\n @if (getColumnSortDirection(column.key) === 'desc') {\n <cide-ele-icon class=\"tw-w-3 tw-h-3 tw-text-blue-600 tw-flex-shrink-0\" title=\"Sorted Descending\">arrow_downward</cide-ele-icon>\n }\n </div>\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-[9999] 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 @let sortDirection = getColumnSortDirection(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'asc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'asc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'asc'\"\n (click)=\"onColumnSort(column, 'asc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"sortDirection === 'asc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'asc'\">arrow_upward</cide-ele-icon>\n Ascending\n </div>\n @if (sortDirection === 'asc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\n </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-transition-colors\"\n [class.tw-text-blue-700]=\"sortDirection === 'desc'\"\n [class.tw-bg-blue-50]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-700]=\"sortDirection !== 'desc'\"\n [class.hover:tw-bg-gray-50]=\"sortDirection !== 'desc'\"\n (click)=\"onColumnSort(column, 'desc')\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\"\n [class.tw-text-blue-600]=\"sortDirection === 'desc'\"\n [class.tw-text-gray-400]=\"sortDirection !== 'desc'\">arrow_downward</cide-ele-icon>\n Descending\n </div>\n @if (sortDirection === 'desc') {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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 @let isGrouped = groupedColumns().includes(column.key);\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-transition-colors\"\n [class.tw-text-blue-700]=\"isGrouped\"\n [class.tw-bg-blue-50]=\"isGrouped\"\n [class.tw-text-gray-700]=\"!isGrouped\"\n [class.hover:tw-bg-gray-50]=\"!isGrouped\"\n (click)=\"onColumnGroupBy(column)\">\n <div class=\"tw-flex tw-items-center\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-mr-3\" \n [class.tw-text-blue-600]=\"isGrouped\"\n [class.tw-text-gray-400]=\"!isGrouped\">group_work</cide-ele-icon>\n Group By Column\n </div>\n @if (isGrouped) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">check</cide-ele-icon>\n }\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-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 visibleColumns(); 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 [style.width]=\"getColumnWidthStyle(column.width)\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width)\">\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 @if (isGroupHeader(item)) {\n <!-- Group Header Row -->\n @let groupKey = getGroupKey(item);\n @let groupLevel = getGroupLevel(item);\n <tr class=\"data-grid-group-row tw-border-b-2 tw-border-gray-300 tw-font-semibold\"\n [class.tw-bg-gray-50]=\"groupLevel === 0\"\n [class.tw-bg-gray-100]=\"groupLevel === 1\"\n [class.tw-bg-gray-200]=\"groupLevel > 1\"\n (click)=\"toggleGroupExpansion(groupKey)\">\n <td class=\"tw-px-3 tw-py-1 tw-cursor-pointer tw-transition-colors\"\n [attr.colspan]=\"visibleColumns().length\">\n <div class=\"tw-flex tw-items-center tw-gap-2\" [style.padding-left.px]=\"groupLevel * 24\">\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-600 tw-cursor-pointer\">\n {{ isGroupExpanded(groupKey) ? 'expand_more' : 'chevron_right' }}\n </cide-ele-icon>\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-blue-600\">group_work</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-900\">\n {{ getGroupColumnHeader(item) }}: {{ getGroupValueDisplay(item) }}\n </span>\n <span class=\"tw-text-xs tw-text-gray-500 tw-ml-2 tw-bg-gray-200 tw-px-2 tw-py-0.5 tw-rounded-full\">\n {{ getGroupRowCount(item) }}\n </span>\n </div>\n </td>\n </tr>\n } @else {\n <!-- Regular Data Row -->\n <tr class=\"tw-group data-grid-row 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 visibleColumns(); 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.width]=\"getColumnWidthStyle(column.width)\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthStyle(column.width) || (getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : null)\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : (getColumnWidthStyle(column.width) ? null : '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 \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"visibleColumns().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\n <!-- Manage Columns Modal -->\n @if (showManageColumnsModal()) {\n <div class=\"tw-fixed tw-inset-0 tw-z-50 tw-overflow-y-auto\" \n (click)=\"closeManageColumnsModal()\"\n style=\"background-color: rgba(0, 0, 0, 0.5);\">\n <div class=\"tw-flex tw-items-center tw-justify-center tw-min-h-screen tw-p-4\">\n <div class=\"tw-bg-white tw-rounded-lg tw-shadow-xl tw-max-w-md tw-w-full tw-max-h-[80vh] tw-overflow-hidden tw-flex tw-flex-col\"\n (click)=\"$event.stopPropagation()\">\n <!-- Modal Header -->\n <div class=\"tw-px-6 tw-py-4 tw-border-b tw-border-gray-200 tw-flex tw-items-center tw-justify-between\">\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-900\">Manage Columns</h3>\n <button type=\"button\"\n class=\"tw-text-gray-400 hover:tw-text-gray-600 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n <cide-ele-icon class=\"tw-w-5 tw-h-5\">close</cide-ele-icon>\n </button>\n </div>\n \n <!-- Modal Body -->\n <div class=\"tw-px-6 tw-py-4 tw-overflow-y-auto tw-flex-1\">\n <p class=\"tw-text-sm tw-text-gray-600 tw-mb-4\">Select columns to show or hide:</p>\n <div class=\"tw-space-y-2\">\n @for (column of mergedConfig().columns; track column.key) {\n <label class=\"tw-flex tw-items-center tw-px-3 tw-py-2 tw-rounded-md hover:tw-bg-gray-50 tw-cursor-pointer tw-transition-colors\">\n <input type=\"checkbox\"\n class=\"tw-mr-3 tw-rounded tw-border-gray-300 tw-text-blue-600 focus:tw-ring-blue-500\"\n [checked]=\"!isColumnHidden(column.key)\"\n (change)=\"toggleColumnVisibility(column.key); $event.stopPropagation()\">\n <span class=\"tw-text-sm tw-text-gray-700 tw-flex-1\">{{ column.header }}</span>\n @if (isColumnHidden(column.key)) {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility_off</cide-ele-icon>\n } @else {\n <cide-ele-icon class=\"tw-w-4 tw-h-4 tw-text-gray-400 tw-ml-2\">visibility</cide-ele-icon>\n }\n </label>\n }\n </div>\n </div>\n \n <!-- Modal Footer -->\n <div class=\"tw-px-6 tw-py-4 tw-border-t tw-border-gray-200 tw-flex tw-items-center tw-justify-end tw-gap-3\">\n <button type=\"button\"\n class=\"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 tw-rounded-md hover:tw-bg-gray-50 tw-transition-colors\"\n (click)=\"closeManageColumnsModal()\">\n Close\n </button>\n <button type=\"button\"\n class=\"tw-px-4 tw-py-2 tw-text-sm tw-font-medium tw-text-white tw-bg-blue-600 tw-border tw-border-transparent tw-rounded-md hover:tw-bg-blue-700 tw-transition-colors\"\n (click)=\"onColumnReset(); closeManageColumnsModal()\">\n Reset All\n </button>\n </div>\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:var(--cide-ele-text-primary);background-color:var(--cide-ele-grid-bg, var(--cide-ele-bg-primary, #ffffff));border-radius:0;overflow:hidden;box-shadow:var(--cide-ele-shadow-sm);-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:var(--cide-ele-scrollbar-thumb, #d1d5db) var(--cide-ele-scrollbar-track, #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:var(--cide-ele-scrollbar-track, #f9fafb)}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:var(--cide-ele-scrollbar-thumb, #d1d5db);border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af)}.data-grid-container table{min-height:300px;border-collapse:separate;border-spacing:0;width:100%;background-color:var(--cide-ele-grid-bg)}.data-grid-container thead{background-color:var(--cide-ele-grid-bg-header);border-bottom:2px solid var(--cide-ele-grid-border-header);position:relative;z-index:100}.data-grid-container thead th{background:transparent;color:var(--cide-ele-grid-text-header);font-weight:500;font-size:12px;text-transform:none;letter-spacing:-.01em;padding:4px 10px;border-bottom:1px solid var(--cide-ele-grid-border);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:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, var(--cide-ele-bg-hover)))}.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:var(--cide-ele-bg-tertiary)}.data-grid-container tbody{background-color:var(--cide-ele-grid-bg, #ffffff)}.data-grid-container tbody td{padding:6px 10px;border-bottom:1px solid var(--cide-ele-grid-border, #e5e7eb);color:var(--cide-ele-grid-text, #1f2937);font-size:13px;vertical-align:middle;line-height:1.5;transition:all .2s cubic-bezier(.4,0,.2,1);background-color:transparent}.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:var(--cide-ele-grid-bg, #ffffff);transition:all .2s cubic-bezier(.4,0,.2,1)}.data-grid-container tbody tr td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container tbody tr:hover,.data-grid-container tbody tr.data-grid-row:hover,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid transparent}.data-grid-container tbody tr:hover td,.data-grid-container tbody tr.data-grid-row:hover td,.data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td{background-color:transparent!important;border-bottom-color:var(--cide-ele-grid-border);color:var(--cide-ele-grid-text)!important}.data-grid-container tbody tr:active{background-color:var(--cide-ele-bg-active);transform:scale(.999)}.data-grid-container tbody tr:active td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-striped);border-left:1px solid transparent}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover{background-color:var(--cide-ele-grid-bg-hover);border-left:2px solid var(--cide-ele-grid-border-hover)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n):hover td{color:var(--cide-ele-grid-text)}.data-grid-container.tw-table-striped tbody tr:nth-child(2n) td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row{background-color:var(--cide-ele-grid-bg-group)!important}.data-grid-container tbody tr.data-grid-group-row td{color:var(--cide-ele-grid-text)}.data-grid-container tbody tr.data-grid-group-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container tbody tr.data-grid-group-row:hover td{color:var(--cide-ele-grid-text)}.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:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.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:2px solid var(--cide-ele-grid-border-header);background-color:var(--cide-ele-grid-bg-header)}.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);position:absolute!important;z-index:9999!important}.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}}.data-grid-container :host .tw-bg-white{background-color:#fff!important}.data-grid-container :host .tw-bg-gray-50{background-color:#fafafa!important}.data-grid-container :host .tw-bg-gray-100{background-color:#f3f4f6!important}.data-grid-container :host thead{background-color:var(--cide-ele-grid-bg-header, #fafafa)!important}.data-grid-container :host thead th{background-color:transparent!important;color:var(--cide-ele-grid-text-header, #6b7280)!important}.data-grid-container :host thead th:hover{background-color:var(--cide-ele-grid-bg-header-hover, var(--cide-ele-grid-bg-hover, #f3f4f6))!important}.data-grid-container :host .data-grid-group-row{background-color:#f3f4f6!important}.data-grid-container :host tbody{background-color:#fff!important}.data-grid-container :host tbody tr{background-color:#fff!important}.data-grid-container :host tbody tr td{background-color:transparent}.data-grid-container :host.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}.data-grid-container :host .tw-text-gray-900{color:#1f2937!important}.data-grid-container :host .tw-text-gray-600{color:#4b5563!important}.data-grid-container :host .tw-text-gray-500{color:#6b7280!important}.data-grid-container :host .tw-text-gray-400{color:#9ca3af!important}.data-grid-container :host .tw-text-gray-700{color:#374151!important}.data-grid-container :host .tw-border-gray-200,.data-grid-container :host .tw-divide-gray-200{border-color:#e5e7eb!important}.data-grid-container :host .tw-border-gray-300{border-color:#d1d5db!important}.data-grid-container :host tbody tr:hover,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.data-grid-container :host tbody tr:hover td,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr:hover .tw-text-gray-900,.data-grid-container :host tbody tr:hover .tw-text-gray-700,.data-grid-container :host tbody tr:hover .tw-text-gray-600,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}.data-grid-container :host tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}.data-grid-container :host .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}.data-grid-container :host .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-white{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-50{background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-bg-gray-100{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .data-grid-group-row{background-color:#f3f4f6!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr{background-color:#fff!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr td{background-color:transparent}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n),::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#fafafa!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-900{color:#1f2937!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-600{color:#4b5563!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-500{color:#6b7280!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-400{color:#9ca3af!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-text-gray-700{color:#374151!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-200,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-divide-gray-200{border-color:#e5e7eb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-border-gray-300{border-color:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-900,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-700,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover)!important;border-left:2px solid var(--cide-ele-grid-border-hover)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-grid-text)!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db!important}::ng-deep :root:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep html:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover,::ng-deep body:not([data-theme=dark]):not(.dark-mode) .data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af!important}.data-grid-container :host-context(.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark]) .tw-bg-white,.data-grid-container :host-context(body.dark-mode) .tw-bg-white,.data-grid-container :host-context(html.dark-mode) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-white,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-white{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-50,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-50,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-50{background-color:var(--cide-ele-grid-bg-header, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark]) .tw-bg-gray-100,.data-grid-container :host-context(body.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context(html.dark-mode) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] body) .tw-bg-gray-100,.data-grid-container :host-context([data-theme=dark] html) .tw-bg-gray-100{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark]) .data-grid-group-row,.data-grid-container :host-context(body.dark-mode) .data-grid-group-row,.data-grid-container :host-context(html.dark-mode) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] body) .data-grid-group-row,.data-grid-container :host-context([data-theme=dark] html) .data-grid-group-row{background-color:var(--cide-ele-grid-bg-group, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark]) tbody,.data-grid-container :host-context(body.dark-mode) tbody,.data-grid-container :host-context(html.dark-mode) tbody,.data-grid-container :host-context([data-theme=dark] body) tbody,.data-grid-container :host-context([data-theme=dark] html) tbody{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark]) tbody tr,.data-grid-container :host-context(body.dark-mode) tbody tr,.data-grid-container :host-context(html.dark-mode) tbody tr,.data-grid-container :host-context([data-theme=dark] body) tbody tr,.data-grid-container :host-context([data-theme=dark] html) tbody tr{background-color:var(--cide-ele-grid-bg, #1f2937)!important}.data-grid-container :host-context(.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark]) tbody tr td,.data-grid-container :host-context(body.dark-mode) tbody tr td,.data-grid-container :host-context(html.dark-mode) tbody tr td,.data-grid-container :host-context([data-theme=dark] body) tbody tr td,.data-grid-container :host-context([data-theme=dark] html) tbody tr td{background-color:transparent}.data-grid-container :host-context(.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark]).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(body.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context(html.dark-mode).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] body).tw-table-striped tbody tr:nth-child(2n),.data-grid-container :host-context([data-theme=dark] html).tw-table-striped tbody tr:nth-child(2n){background-color:var(--cide-ele-grid-bg-striped, #111827)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-900{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-600{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-500,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-500,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-500,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-500{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-400,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-400,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-400,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-400{color:var(--cide-ele-text-tertiary, #9ca3af)!important}.data-grid-container :host-context(.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) .tw-text-gray-700{color:var(--cide-ele-text-secondary, #d1d5db)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark]) .tw-divide-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(body.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-200,.data-grid-container :host-context(html.dark-mode) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] body) .tw-divide-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-200,.data-grid-container :host-context([data-theme=dark] html) .tw-divide-gray-200{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark]) .tw-border-gray-300,.data-grid-container :host-context(body.dark-mode) .tw-border-gray-300,.data-grid-container :host-context(html.dark-mode) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] body) .tw-border-gray-300,.data-grid-container :host-context([data-theme=dark] html) .tw-border-gray-300{border-color:var(--cide-ele-grid-border, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover,.data-grid-container :host-context(body.dark-mode) tbody tr:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context(html.dark-mode) tbody tr:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark]) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(body.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context(html.dark-mode) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] body) tbody tr.data-grid-row:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.hover\\:tw-bg-gray-50:hover .tw-text-gray-600,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-900,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-700,.data-grid-container :host-context([data-theme=dark] html) tbody tr.data-grid-row:hover .tw-text-gray-600{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important;border-left:2px solid var(--cide-ele-grid-border-hover, #60a5fa)!important}.data-grid-container :host-context(.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark]) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(body.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context(html.dark-mode) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] body) tbody tr.tw-cursor-pointer:hover td,.data-grid-container :host-context([data-theme=dark] html) tbody tr.tw-cursor-pointer:hover td{background-color:transparent!important;color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover{background-color:var(--cide-ele-grid-bg-hover, #374151)!important}.data-grid-container :host-context(.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark]).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(body.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context(html.dark-mode).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] body).drag-drop-enabled tbody tr[draggable=true]:hover td,.data-grid-container :host-context([data-theme=dark] html).drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-text-primary, #f9fafb)!important}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:var(--cide-ele-grid-bg-hover, #f9fafb)}.data-grid-container.drag-drop-enabled tbody tr:hover td{color:var(--cide-ele-grid-text, #1f2937)}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed var(--cide-ele-brand-primary, #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:var(--cide-ele-grid-bg-hover, #f9fafb);box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover td{color:var(--cide-ele-grid-text, #1f2937)}.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:var(--cide-ele-bg-tertiary);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:var(--cide-ele-border-primary)}.tree-level-0{border-bottom:2px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-grid-bg)}.tree-level-0:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-grid-bg-striped)}.tree-level-1:hover{background-color:var(--cide-ele-grid-bg-hover)!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid var(--cide-ele-border-secondary)!important;background-color:var(--cide-ele-bg-secondary)}.tree-level-2:hover{background-color:var(--cide-ele-bg-hover)!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid var(--cide-ele-border-primary)!important;background-color:var(--cide-ele-bg-primary)}.tree-level-deep:hover{background-color:var(--cide-ele-bg-hover)!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"] }]
|
|
12107
12191
|
}], propDecorators: { config: [{
|
|
12108
12192
|
type: Input
|
|
12109
12193
|
}], templateRenderers: [{
|
|
@@ -12919,7 +13003,7 @@ class CideEleFloatingContainerComponent {
|
|
|
12919
13003
|
</div>
|
|
12920
13004
|
}
|
|
12921
13005
|
</div>
|
|
12922
|
-
`, 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
|
|
13006
|
+
`, 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:var(--cide-ele-scrollbar-thumb, #d1d5db) var(--cide-ele-scrollbar-track, #f9fafb);position:relative;flex:1;display:flex;flex-direction:column}.floating-container-content::-webkit-scrollbar{width:8px}.floating-container-content::-webkit-scrollbar-track{background:var(--cide-ele-scrollbar-track, #f9fafb);border-radius:4px;margin:4px}.floating-container-content::-webkit-scrollbar-thumb{background:var(--cide-ele-scrollbar-thumb, #d1d5db);border-radius:4px;transition:background-color .2s ease;border:1px solid var(--cide-ele-border-primary, #e5e7eb)}.floating-container-content::-webkit-scrollbar-thumb:hover{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af);border-color:var(--cide-ele-border-secondary, #d1d5db)}.floating-container-content::-webkit-scrollbar-thumb:active{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton], cide-ele-button", 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"] }] });
|
|
12923
13007
|
}
|
|
12924
13008
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingContainerComponent, decorators: [{
|
|
12925
13009
|
type: Component,
|
|
@@ -13029,7 +13113,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
13029
13113
|
</div>
|
|
13030
13114
|
}
|
|
13031
13115
|
</div>
|
|
13032
|
-
`, 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
|
|
13116
|
+
`, 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:var(--cide-ele-scrollbar-thumb, #d1d5db) var(--cide-ele-scrollbar-track, #f9fafb);position:relative;flex:1;display:flex;flex-direction:column}.floating-container-content::-webkit-scrollbar{width:8px}.floating-container-content::-webkit-scrollbar-track{background:var(--cide-ele-scrollbar-track, #f9fafb);border-radius:4px;margin:4px}.floating-container-content::-webkit-scrollbar-thumb{background:var(--cide-ele-scrollbar-thumb, #d1d5db);border-radius:4px;transition:background-color .2s ease;border:1px solid var(--cide-ele-border-primary, #e5e7eb)}.floating-container-content::-webkit-scrollbar-thumb:hover{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af);border-color:var(--cide-ele-border-secondary, #d1d5db)}.floating-container-content::-webkit-scrollbar-thumb:active{background:var(--cide-ele-scrollbar-thumb-hover, #9ca3af)}\n"] }]
|
|
13033
13117
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
13034
13118
|
type: Input
|
|
13035
13119
|
}], isMinimized: [{
|
|
@@ -14246,5 +14330,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
14246
14330
|
* Generated bundle index. Do not edit.
|
|
14247
14331
|
*/
|
|
14248
14332
|
|
|
14249
|
-
export { CapitalizePipe, CideCoreFileManagerService, CideEleBreadcrumbComponent, CideEleButtonComponent, CideEleConfirmationModalComponent, CideEleDataGridComponent, CideEleDropdownComponent, CideEleFileImageDirective, CideEleFileInputComponent, CideEleFileManagerService, CideEleFloatingContainerComponent, CideEleFloatingContainerDynamicDirective, CideEleFloatingContainerManagerComponent, CideEleFloatingContainerService, CideEleFloatingFeaturesService, CideEleFloatingFileUploaderComponent, CideEleFloatingFileUploaderService, CideEleGlobalNotificationsComponent, CideEleJsonEditorComponent, CideEleResizerDirective, CideEleSkeletonLoaderComponent, CideEleTabComponent, CideEleToastNotificationComponent, CideElementsService, CideFormFieldErrorComponent, CideIconComponent, CideInputComponent, CideSelectComponent, CideSelectOptionComponent, CideSpinnerComponent, CideTextareaComponent, CideThemeService, ConfirmationService, CoreFileManagerInsertUpdatePayload, DEFAULT_GRID_CONFIG, DropdownManagerService, ExportService, ICoreCyfmSave, KeyboardShortcutService, MFileManager, NotificationApiService, NotificationService, PortalService, TooltipDirective, WebSocketNotificationService, cidePath, hostManagerRoutesUrl, notificationRoutesUrl };
|
|
14333
|
+
export { CapitalizePipe, CideCoreFileManagerService, CideEleBreadcrumbComponent, CideEleButtonComponent, CideEleConfirmationModalComponent, CideEleDataGridComponent, CideEleDropdownComponent, CideEleFileImageDirective, CideEleFileInputComponent, CideEleFileManagerService, CideEleFloatingContainerComponent, CideEleFloatingContainerDynamicDirective, CideEleFloatingContainerManagerComponent, CideEleFloatingContainerService, CideEleFloatingFeaturesService, CideEleFloatingFileUploaderComponent, CideEleFloatingFileUploaderService, CideEleGlobalNotificationsComponent, CideEleJsonEditorComponent, CideEleResizerDirective, CideEleSkeletonLoaderComponent, CideEleTabComponent, CideEleThemeToggleComponent, CideEleToastNotificationComponent, CideElementsService, CideFormFieldErrorComponent, CideIconComponent, CideInputComponent, CideSelectComponent, CideSelectOptionComponent, CideSpinnerComponent, CideTextareaComponent, CideThemeService, ConfirmationService, CoreFileManagerInsertUpdatePayload, DEFAULT_GRID_CONFIG, DropdownManagerService, ExportService, ICoreCyfmSave, KeyboardShortcutService, MFileManager, NotificationApiService, NotificationService, PortalService, TooltipDirective, WebSocketNotificationService, cidePath, hostManagerRoutesUrl, notificationRoutesUrl };
|
|
14250
14334
|
//# sourceMappingURL=cloud-ide-element.mjs.map
|