@ascentgl/ads-ui 21.57.0 → 21.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7135,10 +7135,16 @@ class AdsColumnSortFilterMenuComponent {
|
|
|
7135
7135
|
return;
|
|
7136
7136
|
}
|
|
7137
7137
|
this.initializedConfigField = this.config.field;
|
|
7138
|
+
// Determine if we should default to all selected or use the provided selectedFilterValues
|
|
7139
|
+
// If selectedFilterValues equals all filterOptions, it means "all selected" (no filter active)
|
|
7140
|
+
// If selectedFilterValues is empty [], it means "none selected" (show nothing)
|
|
7141
|
+
// If selectedFilterValues has some values, use those
|
|
7142
|
+
const allOptionsSelected = this.selectedFilterValues.length === this.config.filterOptions.length &&
|
|
7143
|
+
this.config.filterOptions.every(opt => this.selectedFilterValues.includes(opt));
|
|
7138
7144
|
const options = this.config.filterOptions.map((value) => {
|
|
7139
|
-
// If
|
|
7140
|
-
// Otherwise, check if value is in selectedFilterValues
|
|
7141
|
-
const isSelected =
|
|
7145
|
+
// If all options match selectedFilterValues, treat as all selected
|
|
7146
|
+
// Otherwise, check if this specific value is in selectedFilterValues
|
|
7147
|
+
const isSelected = allOptionsSelected || this.selectedFilterValues.includes(value);
|
|
7142
7148
|
const control = new FormControl(isSelected, { nonNullable: true });
|
|
7143
7149
|
// Subscribe to control value changes
|
|
7144
7150
|
const subscription = control.valueChanges.subscribe(() => {
|
|
@@ -7266,11 +7272,11 @@ class AdsColumnSortFilterMenuComponent {
|
|
|
7266
7272
|
return text.length > this.MAX_LABEL_LENGTH;
|
|
7267
7273
|
}
|
|
7268
7274
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdsColumnSortFilterMenuComponent, deps: [{ token: i1.AdsIconRegistry }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7269
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: AdsColumnSortFilterMenuComponent, isStandalone: false, selector: "ads-column-sort-filter-menu", inputs: { config: "config", currentSortDirection: "currentSortDirection", selectedFilterValues: "selectedFilterValues" }, outputs: { sortChanged: "sortChanged", filterChanged: "filterChanged", hideColumn: "hideColumn" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"column-sort-filter-menu\"\n (click)=\"$event.stopPropagation()\"\n [class.full-height]=\"hasFilterOptions && hasSortOptions\"\n>\n <!-- Sort Options -->\n @if (hasSortOptions) {\n <div class=\"sort-section\">\n @for (sortOption of sortOptions; track sortOption.direction) {\n <div\n class=\"sort-option\"\n [class.active]=\"isSortActive(sortOption.direction)\"\n (click)=\"onSortOptionClick(sortOption.direction)\"\n >\n <ads-icon\n [name]=\"sortOption.direction === 'asc' ? 'sort_down_to_up' : 'sort_up_to_down'\"\n [theme]=\"isSortActive(sortOption.direction) ? 'secondary' : 'iconPrimary'\"\n size=\"xxs_16\"\n class=\"sort-icon\"\n />\n <span class=\"sort-label\">{{ sortOption.label }}</span>\n @if (isSortActive(sortOption.direction)) {\n <ads-icon\n name=\"check_circle_filled\"\n theme=\"secondary\"\n size=\"xxs_16\"\n />\n }\n </div>\n }\n </div>\n }\n\n @if (hasFilterOptions && hasSortOptions) {\n <ads-divider />\n }\n\n <!-- Filter Section -->\n @if (hasFilterOptions) {\n <div class=\"filter-section\">\n <ads-search-input\n [control]=\"searchControl\"\n placeholder=\"Filter Search\"\n [showFooter]=\"false\"\n />\n\n @if (noResultsFound()) {\n <p class=\"no-results\">\n Can't find \"{{ searchValue() }}.\" Try searching something else.\n </p>\n } @else {\n <div class=\"filter-options\">\n <!-- Select All - only show when not searching -->\n @if (!searchValue()) {\n <ads-checkbox\n [control]=\"selectAllControl\"\n label=\"Select All\"\n [showFooter]=\"false\"\n />\n }\n\n <!-- Filter Options List -->\n @for (option of filteredOptions(); track option.value) {\n <div class=\"filter-option-wrapper\" [matTooltip]=\"option.label\" [matTooltipDisabled]=\"!isTextTruncated(option.label)\">\n <ads-checkbox\n [control]=\"option.control\"\n [label]=\"option.label\"\n [showFooter]=\"false\"\n />\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Hide Column -->\n <div class=\"hide-column-section\" (click)=\"onHideColumnClick()\">\n <ads-icon name=\"visibility_eye_none\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"xxs_16\" />\n <span>Hide Column</span>\n </div>\n</div>\n\n", styles: [".column-sort-filter-menu{width:234px;max-height:358px;background-color:var(--color-white);display:flex;flex-direction:column}.column-sort-filter-menu.full-height{height:358px}.column-sort-filter-menu .sort-section{padding:0;flex-shrink:0}.column-sort-filter-menu .sort-section .sort-option{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease}.column-sort-filter-menu .sort-section .sort-option .sort-label{font-size:16px;line-height:21px;color:var(--color-dark)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active){background-color:var(--color-secondary-hover)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-label{color:var(--color-white)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-icon ::ng-deep svg{fill:var(--color-white)!important}.column-sort-filter-menu .sort-section .sort-option.active{background-color:var(--color-secondary-10)}.column-sort-filter-menu .filter-section{padding:0 12px;flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.column-sort-filter-menu .filter-section ads-search-input{display:block;padding:12px 0;flex-shrink:0}.column-sort-filter-menu .filter-section .no-results{color:var(--color-error);font-size:12px;line-height:16px;padding:12px 0;font-weight:600;margin:0}.column-sort-filter-menu .filter-section .filter-options{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox{display:block}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox ::ng-deep .checkbox-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.column-sort-filter-menu .hide-column-section{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease;flex-shrink:0;border-top:1px solid var(--color-light)}.column-sort-filter-menu .hide-column-section:hover{background-color:var(--color-secondary-hover)}.column-sort-filter-menu .hide-column-section:hover span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:hover ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section:active{background-color:var(--color-secondary-pressed)}.column-sort-filter-menu .hide-column-section:active span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:active ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section span{font-size:16px;line-height:21px;color:var(--color-dark)}\n"], dependencies: [{ kind: "component", type: i1.AdsIconComponent, selector: "ads-icon", inputs: ["size", "name", "color", "theme", "stroke"] }, { kind: "component", type: AdsSearchInputComponent, selector: "ads-search-input", inputs: ["searchCallback", "isIconClickable", "searchEmptyValue", "loading"] }, { kind: "component", type: AdsCheckboxComponent, selector: "ads-checkbox", inputs: ["indeterminate", "width", "tooltip", "tooltipHref", "size"] }, { kind: "component", type: DividerComponent, selector: "ads-divider", inputs: ["margin", "color"] }, { kind: "directive", type: i13.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
7275
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: AdsColumnSortFilterMenuComponent, isStandalone: false, selector: "ads-column-sort-filter-menu", inputs: { config: "config", currentSortDirection: "currentSortDirection", selectedFilterValues: "selectedFilterValues" }, outputs: { sortChanged: "sortChanged", filterChanged: "filterChanged", hideColumn: "hideColumn" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"column-sort-filter-menu\"\n (click)=\"$event.stopPropagation()\"\n [class.full-height]=\"hasFilterOptions && hasSortOptions\"\n>\n <!-- Sort Options -->\n @if (hasSortOptions) {\n <div class=\"sort-section\">\n @for (sortOption of sortOptions; track sortOption.direction) {\n <div\n class=\"sort-option\"\n [class.active]=\"isSortActive(sortOption.direction)\"\n (click)=\"onSortOptionClick(sortOption.direction)\"\n >\n <ads-icon\n [name]=\"sortOption.direction === 'asc' ? 'sort_down_to_up' : 'sort_up_to_down'\"\n [theme]=\"isSortActive(sortOption.direction) ? 'secondary' : 'iconPrimary'\"\n size=\"xxs_16\"\n class=\"sort-icon\"\n />\n <span class=\"sort-label\">{{ sortOption.label }}</span>\n @if (isSortActive(sortOption.direction)) {\n <ads-icon\n name=\"check_circle_filled\"\n theme=\"secondary\"\n size=\"xxs_16\"\n />\n }\n </div>\n }\n </div>\n }\n\n @if (hasFilterOptions && hasSortOptions) {\n <ads-divider />\n }\n\n <!-- Filter Section -->\n @if (hasFilterOptions) {\n <div class=\"filter-section\">\n <ads-search-input\n [control]=\"searchControl\"\n placeholder=\"Filter Search\"\n [showFooter]=\"false\"\n />\n\n @if (noResultsFound()) {\n <p class=\"no-results\">\n Can't find \"{{ searchValue() }}.\" Try searching something else.\n </p>\n } @else {\n <div class=\"filter-options\">\n <!-- Select All - only show when not searching -->\n @if (!searchValue()) {\n <ads-checkbox\n [control]=\"selectAllControl\"\n label=\"Select All\"\n [showFooter]=\"false\"\n />\n }\n\n <!-- Filter Options List -->\n @for (option of filteredOptions(); track option.value) {\n <div class=\"filter-option-wrapper\" [matTooltip]=\"option.label\" [matTooltipDisabled]=\"!isTextTruncated(option.label)\">\n <ads-checkbox\n [control]=\"option.control\"\n [label]=\"option.label\"\n [showFooter]=\"false\"\n />\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Hide Column -->\n <div class=\"hide-column-section\" (click)=\"onHideColumnClick()\">\n <ads-icon name=\"visibility_eye_none\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"xxs_16\" />\n <span>Hide Column</span>\n </div>\n</div>\n\n", styles: [".column-sort-filter-menu{width:234px;max-height:358px;background-color:var(--color-white);display:flex;flex-direction:column}.column-sort-filter-menu.full-height{height:358px}.column-sort-filter-menu .sort-section{padding:0;flex-shrink:0}.column-sort-filter-menu .sort-section .sort-option{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease}.column-sort-filter-menu .sort-section .sort-option .sort-label{font-size:16px;line-height:21px;color:var(--color-dark)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active){background-color:var(--color-secondary-hover)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-label{color:var(--color-white)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-icon ::ng-deep svg{fill:var(--color-white)!important}.column-sort-filter-menu .sort-section .sort-option.active{background-color:var(--color-secondary-10)}.column-sort-filter-menu .filter-section{padding:0 12px;flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.column-sort-filter-menu .filter-section ads-search-input{display:block;padding:12px 0;flex-shrink:0}.column-sort-filter-menu .filter-section .no-results{color:var(--color-error);font-size:12px;line-height:16px;padding:12px 0;font-weight:600;margin:0}.column-sort-filter-menu .filter-section .filter-options{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox{display:block}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox ::ng-deep .checkbox-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.column-sort-filter-menu .hide-column-section{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease;flex-shrink:0;border-top:1px solid var(--color-light)}.column-sort-filter-menu .hide-column-section:hover{background-color:var(--color-secondary-hover)}.column-sort-filter-menu .hide-column-section:hover span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:hover ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section:active{background-color:var(--color-secondary-pressed)}.column-sort-filter-menu .hide-column-section:active span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:active ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section span{font-size:16px;line-height:21px;color:var(--color-dark)}\n"], dependencies: [{ kind: "component", type: i1.AdsIconComponent, selector: "ads-icon", inputs: ["size", "name", "color", "theme", "stroke"] }, { kind: "component", type: AdsSearchInputComponent, selector: "ads-search-input", inputs: ["searchCallback", "isIconClickable", "searchEmptyValue", "loading"] }, { kind: "component", type: AdsCheckboxComponent, selector: "ads-checkbox", inputs: ["indeterminate", "width", "tooltip", "tooltipHref", "size"] }, { kind: "component", type: DividerComponent, selector: "ads-divider", inputs: ["margin", "color"] }, { kind: "directive", type: i13.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7270
7276
|
}
|
|
7271
7277
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdsColumnSortFilterMenuComponent, decorators: [{
|
|
7272
7278
|
type: Component,
|
|
7273
|
-
args: [{ selector: 'ads-column-sort-filter-menu', standalone: false, template: "<div\n class=\"column-sort-filter-menu\"\n (click)=\"$event.stopPropagation()\"\n [class.full-height]=\"hasFilterOptions && hasSortOptions\"\n>\n <!-- Sort Options -->\n @if (hasSortOptions) {\n <div class=\"sort-section\">\n @for (sortOption of sortOptions; track sortOption.direction) {\n <div\n class=\"sort-option\"\n [class.active]=\"isSortActive(sortOption.direction)\"\n (click)=\"onSortOptionClick(sortOption.direction)\"\n >\n <ads-icon\n [name]=\"sortOption.direction === 'asc' ? 'sort_down_to_up' : 'sort_up_to_down'\"\n [theme]=\"isSortActive(sortOption.direction) ? 'secondary' : 'iconPrimary'\"\n size=\"xxs_16\"\n class=\"sort-icon\"\n />\n <span class=\"sort-label\">{{ sortOption.label }}</span>\n @if (isSortActive(sortOption.direction)) {\n <ads-icon\n name=\"check_circle_filled\"\n theme=\"secondary\"\n size=\"xxs_16\"\n />\n }\n </div>\n }\n </div>\n }\n\n @if (hasFilterOptions && hasSortOptions) {\n <ads-divider />\n }\n\n <!-- Filter Section -->\n @if (hasFilterOptions) {\n <div class=\"filter-section\">\n <ads-search-input\n [control]=\"searchControl\"\n placeholder=\"Filter Search\"\n [showFooter]=\"false\"\n />\n\n @if (noResultsFound()) {\n <p class=\"no-results\">\n Can't find \"{{ searchValue() }}.\" Try searching something else.\n </p>\n } @else {\n <div class=\"filter-options\">\n <!-- Select All - only show when not searching -->\n @if (!searchValue()) {\n <ads-checkbox\n [control]=\"selectAllControl\"\n label=\"Select All\"\n [showFooter]=\"false\"\n />\n }\n\n <!-- Filter Options List -->\n @for (option of filteredOptions(); track option.value) {\n <div class=\"filter-option-wrapper\" [matTooltip]=\"option.label\" [matTooltipDisabled]=\"!isTextTruncated(option.label)\">\n <ads-checkbox\n [control]=\"option.control\"\n [label]=\"option.label\"\n [showFooter]=\"false\"\n />\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Hide Column -->\n <div class=\"hide-column-section\" (click)=\"onHideColumnClick()\">\n <ads-icon name=\"visibility_eye_none\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"xxs_16\" />\n <span>Hide Column</span>\n </div>\n</div>\n\n", styles: [".column-sort-filter-menu{width:234px;max-height:358px;background-color:var(--color-white);display:flex;flex-direction:column}.column-sort-filter-menu.full-height{height:358px}.column-sort-filter-menu .sort-section{padding:0;flex-shrink:0}.column-sort-filter-menu .sort-section .sort-option{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease}.column-sort-filter-menu .sort-section .sort-option .sort-label{font-size:16px;line-height:21px;color:var(--color-dark)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active){background-color:var(--color-secondary-hover)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-label{color:var(--color-white)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-icon ::ng-deep svg{fill:var(--color-white)!important}.column-sort-filter-menu .sort-section .sort-option.active{background-color:var(--color-secondary-10)}.column-sort-filter-menu .filter-section{padding:0 12px;flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.column-sort-filter-menu .filter-section ads-search-input{display:block;padding:12px 0;flex-shrink:0}.column-sort-filter-menu .filter-section .no-results{color:var(--color-error);font-size:12px;line-height:16px;padding:12px 0;font-weight:600;margin:0}.column-sort-filter-menu .filter-section .filter-options{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox{display:block}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox ::ng-deep .checkbox-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.column-sort-filter-menu .hide-column-section{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease;flex-shrink:0;border-top:1px solid var(--color-light)}.column-sort-filter-menu .hide-column-section:hover{background-color:var(--color-secondary-hover)}.column-sort-filter-menu .hide-column-section:hover span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:hover ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section:active{background-color:var(--color-secondary-pressed)}.column-sort-filter-menu .hide-column-section:active span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:active ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section span{font-size:16px;line-height:21px;color:var(--color-dark)}\n"] }]
|
|
7279
|
+
args: [{ selector: 'ads-column-sort-filter-menu', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"column-sort-filter-menu\"\n (click)=\"$event.stopPropagation()\"\n [class.full-height]=\"hasFilterOptions && hasSortOptions\"\n>\n <!-- Sort Options -->\n @if (hasSortOptions) {\n <div class=\"sort-section\">\n @for (sortOption of sortOptions; track sortOption.direction) {\n <div\n class=\"sort-option\"\n [class.active]=\"isSortActive(sortOption.direction)\"\n (click)=\"onSortOptionClick(sortOption.direction)\"\n >\n <ads-icon\n [name]=\"sortOption.direction === 'asc' ? 'sort_down_to_up' : 'sort_up_to_down'\"\n [theme]=\"isSortActive(sortOption.direction) ? 'secondary' : 'iconPrimary'\"\n size=\"xxs_16\"\n class=\"sort-icon\"\n />\n <span class=\"sort-label\">{{ sortOption.label }}</span>\n @if (isSortActive(sortOption.direction)) {\n <ads-icon\n name=\"check_circle_filled\"\n theme=\"secondary\"\n size=\"xxs_16\"\n />\n }\n </div>\n }\n </div>\n }\n\n @if (hasFilterOptions && hasSortOptions) {\n <ads-divider />\n }\n\n <!-- Filter Section -->\n @if (hasFilterOptions) {\n <div class=\"filter-section\">\n <ads-search-input\n [control]=\"searchControl\"\n placeholder=\"Filter Search\"\n [showFooter]=\"false\"\n />\n\n @if (noResultsFound()) {\n <p class=\"no-results\">\n Can't find \"{{ searchValue() }}.\" Try searching something else.\n </p>\n } @else {\n <div class=\"filter-options\">\n <!-- Select All - only show when not searching -->\n @if (!searchValue()) {\n <ads-checkbox\n [control]=\"selectAllControl\"\n label=\"Select All\"\n [showFooter]=\"false\"\n />\n }\n\n <!-- Filter Options List -->\n @for (option of filteredOptions(); track option.value) {\n <div class=\"filter-option-wrapper\" [matTooltip]=\"option.label\" [matTooltipDisabled]=\"!isTextTruncated(option.label)\">\n <ads-checkbox\n [control]=\"option.control\"\n [label]=\"option.label\"\n [showFooter]=\"false\"\n />\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Hide Column -->\n <div class=\"hide-column-section\" (click)=\"onHideColumnClick()\">\n <ads-icon name=\"visibility_eye_none\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"xxs_16\" />\n <span>Hide Column</span>\n </div>\n</div>\n\n", styles: [".column-sort-filter-menu{width:234px;max-height:358px;background-color:var(--color-white);display:flex;flex-direction:column}.column-sort-filter-menu.full-height{height:358px}.column-sort-filter-menu .sort-section{padding:0;flex-shrink:0}.column-sort-filter-menu .sort-section .sort-option{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease}.column-sort-filter-menu .sort-section .sort-option .sort-label{font-size:16px;line-height:21px;color:var(--color-dark)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active){background-color:var(--color-secondary-hover)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-label{color:var(--color-white)}.column-sort-filter-menu .sort-section .sort-option:hover:not(.active) .sort-icon ::ng-deep svg{fill:var(--color-white)!important}.column-sort-filter-menu .sort-section .sort-option.active{background-color:var(--color-secondary-10)}.column-sort-filter-menu .filter-section{padding:0 12px;flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.column-sort-filter-menu .filter-section ads-search-input{display:block;padding:12px 0;flex-shrink:0}.column-sort-filter-menu .filter-section .no-results{color:var(--color-error);font-size:12px;line-height:16px;padding:12px 0;font-weight:600;margin:0}.column-sort-filter-menu .filter-section .filter-options{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox{display:block}.column-sort-filter-menu .filter-section .filter-options .filter-option-wrapper ads-checkbox ::ng-deep .checkbox-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.column-sort-filter-menu .hide-column-section{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;transition:background-color .2s ease;flex-shrink:0;border-top:1px solid var(--color-light)}.column-sort-filter-menu .hide-column-section:hover{background-color:var(--color-secondary-hover)}.column-sort-filter-menu .hide-column-section:hover span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:hover ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section:active{background-color:var(--color-secondary-pressed)}.column-sort-filter-menu .hide-column-section:active span{color:var(--color-white)}.column-sort-filter-menu .hide-column-section:active ads-icon ::ng-deep svg{fill:var(--color-white)!important;stroke:var(--color-white)!important}.column-sort-filter-menu .hide-column-section span{font-size:16px;line-height:21px;color:var(--color-dark)}\n"] }]
|
|
7274
7280
|
}], ctorParameters: () => [{ type: i1.AdsIconRegistry }], propDecorators: { config: [{
|
|
7275
7281
|
type: Input
|
|
7276
7282
|
}], currentSortDirection: [{
|
|
@@ -7345,13 +7351,15 @@ class AdsCustomHeaderComponent {
|
|
|
7345
7351
|
}
|
|
7346
7352
|
closeMenu() {
|
|
7347
7353
|
this.menuOpen = false;
|
|
7354
|
+
// Notify parent that menu was closed (for pending header refresh)
|
|
7355
|
+
this.params.onMenuClosed?.();
|
|
7348
7356
|
}
|
|
7349
7357
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdsCustomHeaderComponent, deps: [{ token: i1.AdsIconRegistry }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7350
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: AdsCustomHeaderComponent, isStandalone: false, selector: "ads-custom-header", ngImport: i0, template: "<div class=\"ads-custom-header\">\n <span\n class=\"menu-trigger-anchor\"\n [class.align-right]=\"isLastColumn\"\n #menuTrigger=\"matMenuTrigger\"\n [matMenuTriggerFor]=\"sortFilterMenu\"\n (menuOpened)=\"menuOpen = true\"\n (menuClosed)=\"
|
|
7358
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: AdsCustomHeaderComponent, isStandalone: false, selector: "ads-custom-header", ngImport: i0, template: "<div class=\"ads-custom-header\">\n <span\n class=\"menu-trigger-anchor\"\n [class.align-right]=\"isLastColumn\"\n #menuTrigger=\"matMenuTrigger\"\n [matMenuTriggerFor]=\"sortFilterMenu\"\n (menuOpened)=\"menuOpen = true\"\n (menuClosed)=\"closeMenu()\"\n ></span>\n\n <span class=\"header-text\">{{ displayName }}</span>\n\n @if (hasSortFilterConfig) {\n <button\n class=\"header-menu-button\"\n [class.active]=\"isActive\"\n (click)=\"menuTrigger.openMenu(); $event.stopPropagation()\"\n >\n @if (sortDirection === 'asc') {\n <ads-icon name=\"sorting_arrow_up\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"auto\" />\n } @else if (sortDirection === 'desc') {\n <ads-icon name=\"sorting_arrow_down\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"auto\" />\n } @else {\n <ads-icon name=\"menu_filters\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"auto\" />\n }\n </button>\n }\n</div>\n\n<mat-menu #sortFilterMenu=\"matMenu\" class=\"column-sort-filter-panel\" [xPosition]=\"isLastColumn ? 'before' : 'after'\">\n @if (sortFilterConfig) {\n <ads-column-sort-filter-menu\n [config]=\"sortFilterConfig\"\n [currentSortDirection]=\"sortDirection\"\n [selectedFilterValues]=\"filterValues\"\n (sortChanged)=\"onSortChanged($event)\"\n (filterChanged)=\"onFilterChanged($event)\"\n (hideColumn)=\"onHideColumn($event)\"\n />\n }\n</mat-menu>\n\n", styles: [":host{display:block;width:100%;height:100%}.ads-custom-header{display:flex;align-items:center;justify-content:flex-start;width:100%;height:100%;position:relative}.ads-custom-header .menu-trigger-anchor{position:absolute;left:0;bottom:0;width:1px;height:1px;pointer-events:none}.ads-custom-header .menu-trigger-anchor.align-right{left:auto;right:0}.ads-custom-header .header-text{font-weight:600;font-size:16px;line-height:21px;color:var(--color-medium);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ads-custom-header .header-menu-button{display:flex;align-items:center;justify-content:center;gap:2px;padding:4px;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s ease;flex-shrink:0}::ng-deep .column-sort-filter-panel{margin-top:1px}::ng-deep .column-sort-filter-panel .mat-mdc-menu-content{padding:0}\n"], dependencies: [{ kind: "component", type: i1.AdsIconComponent, selector: "ads-icon", inputs: ["size", "name", "color", "theme", "stroke"] }, { kind: "component", type: i4$3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i4$3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: AdsColumnSortFilterMenuComponent, selector: "ads-column-sort-filter-menu", inputs: ["config", "currentSortDirection", "selectedFilterValues"], outputs: ["sortChanged", "filterChanged", "hideColumn"] }] }); }
|
|
7351
7359
|
}
|
|
7352
7360
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdsCustomHeaderComponent, decorators: [{
|
|
7353
7361
|
type: Component,
|
|
7354
|
-
args: [{ selector: 'ads-custom-header', standalone: false, template: "<div class=\"ads-custom-header\">\n <span\n class=\"menu-trigger-anchor\"\n [class.align-right]=\"isLastColumn\"\n #menuTrigger=\"matMenuTrigger\"\n [matMenuTriggerFor]=\"sortFilterMenu\"\n (menuOpened)=\"menuOpen = true\"\n (menuClosed)=\"
|
|
7362
|
+
args: [{ selector: 'ads-custom-header', standalone: false, template: "<div class=\"ads-custom-header\">\n <span\n class=\"menu-trigger-anchor\"\n [class.align-right]=\"isLastColumn\"\n #menuTrigger=\"matMenuTrigger\"\n [matMenuTriggerFor]=\"sortFilterMenu\"\n (menuOpened)=\"menuOpen = true\"\n (menuClosed)=\"closeMenu()\"\n ></span>\n\n <span class=\"header-text\">{{ displayName }}</span>\n\n @if (hasSortFilterConfig) {\n <button\n class=\"header-menu-button\"\n [class.active]=\"isActive\"\n (click)=\"menuTrigger.openMenu(); $event.stopPropagation()\"\n >\n @if (sortDirection === 'asc') {\n <ads-icon name=\"sorting_arrow_up\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"auto\" />\n } @else if (sortDirection === 'desc') {\n <ads-icon name=\"sorting_arrow_down\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"auto\" />\n } @else {\n <ads-icon name=\"menu_filters\" theme=\"iconPrimary\" stroke=\"iconPrimary\" size=\"auto\" />\n }\n </button>\n }\n</div>\n\n<mat-menu #sortFilterMenu=\"matMenu\" class=\"column-sort-filter-panel\" [xPosition]=\"isLastColumn ? 'before' : 'after'\">\n @if (sortFilterConfig) {\n <ads-column-sort-filter-menu\n [config]=\"sortFilterConfig\"\n [currentSortDirection]=\"sortDirection\"\n [selectedFilterValues]=\"filterValues\"\n (sortChanged)=\"onSortChanged($event)\"\n (filterChanged)=\"onFilterChanged($event)\"\n (hideColumn)=\"onHideColumn($event)\"\n />\n }\n</mat-menu>\n\n", styles: [":host{display:block;width:100%;height:100%}.ads-custom-header{display:flex;align-items:center;justify-content:flex-start;width:100%;height:100%;position:relative}.ads-custom-header .menu-trigger-anchor{position:absolute;left:0;bottom:0;width:1px;height:1px;pointer-events:none}.ads-custom-header .menu-trigger-anchor.align-right{left:auto;right:0}.ads-custom-header .header-text{font-weight:600;font-size:16px;line-height:21px;color:var(--color-medium);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ads-custom-header .header-menu-button{display:flex;align-items:center;justify-content:center;gap:2px;padding:4px;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s ease;flex-shrink:0}::ng-deep .column-sort-filter-panel{margin-top:1px}::ng-deep .column-sort-filter-panel .mat-mdc-menu-content{padding:0}\n"] }]
|
|
7355
7363
|
}], ctorParameters: () => [{ type: i1.AdsIconRegistry }] });
|
|
7356
7364
|
|
|
7357
7365
|
class AdsColumnSortFilterMenuModule {
|
|
@@ -7501,20 +7509,43 @@ class AdsTableComponent {
|
|
|
7501
7509
|
adsCustomHeader: AdsCustomHeaderComponent,
|
|
7502
7510
|
};
|
|
7503
7511
|
/** @ignore */
|
|
7504
|
-
this.filteredColumns = [];
|
|
7512
|
+
this.filteredColumns = signal([], ...(ngDevMode ? [{ debugName: "filteredColumns" }] : []));
|
|
7513
|
+
/** @ignore - Flag indicating headers need refresh when menu closes (for dynamic filter options) */
|
|
7514
|
+
this.pendingHeaderRefresh = false;
|
|
7505
7515
|
/** @ignore */
|
|
7506
7516
|
this.columnVisibilityMenuOpen = false;
|
|
7517
|
+
/** @ignore - Cache for filtered column values to avoid recalculating on every access */
|
|
7518
|
+
this.filteredColumnValuesCache = new Map();
|
|
7519
|
+
/** @ignore - Cache version to invalidate cache when filters change */
|
|
7520
|
+
this.filterCacheVersion = 0;
|
|
7521
|
+
/** @ignore - Cache for unique column values (all values, not filtered) */
|
|
7522
|
+
this.uniqueColumnValuesCache = new Map();
|
|
7523
|
+
/** @ignore - Data version to invalidate unique values cache when rowData changes */
|
|
7524
|
+
this.dataVersion = 0;
|
|
7507
7525
|
/** @ignore */
|
|
7508
7526
|
this.isExternalFilterPresent = () => {
|
|
7509
7527
|
// Check if any custom column filter is active
|
|
7510
7528
|
for (const [field, values] of this.columnFilterStates()) {
|
|
7511
|
-
|
|
7512
|
-
if (
|
|
7513
|
-
|
|
7514
|
-
|
|
7529
|
+
// If no values selected, filter is active (show nothing)
|
|
7530
|
+
if (values.length === 0) {
|
|
7531
|
+
return true;
|
|
7532
|
+
}
|
|
7533
|
+
// Check if this column has explicit config in columnSortFilterConfigs
|
|
7534
|
+
const explicitConfig = this.columnSortFilterConfigs.find(c => c.field === field);
|
|
7535
|
+
if (explicitConfig) {
|
|
7536
|
+
// For columns with explicit config, check if not all options are selected
|
|
7537
|
+
// Use the original filterOptions from config if provided, otherwise get unique values
|
|
7538
|
+
const baseFilterOptions = explicitConfig.filterOptions && explicitConfig.filterOptions.length > 0
|
|
7539
|
+
? explicitConfig.filterOptions
|
|
7540
|
+
: this.getUniqueColumnValues(field);
|
|
7541
|
+
if (baseFilterOptions.length > 0 && values.length < baseFilterOptions.length) {
|
|
7515
7542
|
return true;
|
|
7516
7543
|
}
|
|
7517
7544
|
}
|
|
7545
|
+
else {
|
|
7546
|
+
// For columns without explicit config, any stored filter state means filter is active
|
|
7547
|
+
return true;
|
|
7548
|
+
}
|
|
7518
7549
|
}
|
|
7519
7550
|
return false;
|
|
7520
7551
|
};
|
|
@@ -7523,17 +7554,25 @@ class AdsTableComponent {
|
|
|
7523
7554
|
if (!node.data)
|
|
7524
7555
|
return true;
|
|
7525
7556
|
for (const [field, selectedValues] of this.columnFilterStates()) {
|
|
7526
|
-
const config = this.getColumnSortFilterConfig(field);
|
|
7527
|
-
if (!config?.filterOptions)
|
|
7528
|
-
continue;
|
|
7529
7557
|
// If no values are selected, hide all rows for this filter
|
|
7530
7558
|
if (selectedValues.length === 0) {
|
|
7531
7559
|
return false;
|
|
7532
7560
|
}
|
|
7533
|
-
//
|
|
7534
|
-
|
|
7535
|
-
|
|
7561
|
+
// Check if this column has explicit config in columnSortFilterConfigs
|
|
7562
|
+
const explicitConfig = this.columnSortFilterConfigs.find(c => c.field === field);
|
|
7563
|
+
if (explicitConfig) {
|
|
7564
|
+
// For columns with explicit config, check if all options are selected
|
|
7565
|
+
// Use the original filterOptions from config if provided, otherwise get unique values
|
|
7566
|
+
const baseFilterOptions = explicitConfig.filterOptions && explicitConfig.filterOptions.length > 0
|
|
7567
|
+
? explicitConfig.filterOptions
|
|
7568
|
+
: this.getUniqueColumnValues(field);
|
|
7569
|
+
if (baseFilterOptions.length > 0 && selectedValues.length === baseFilterOptions.length) {
|
|
7570
|
+
// All values selected = no filter, skip this column
|
|
7571
|
+
continue;
|
|
7572
|
+
}
|
|
7536
7573
|
}
|
|
7574
|
+
// For columns without explicit config (like supplier, destination),
|
|
7575
|
+
// always apply the filter based on selectedValues
|
|
7537
7576
|
const cellValue = node.data[field];
|
|
7538
7577
|
const cellValueStr = cellValue != null ? String(cellValue) : '';
|
|
7539
7578
|
if (!selectedValues.includes(cellValueStr)) {
|
|
@@ -7589,6 +7628,7 @@ class AdsTableComponent {
|
|
|
7589
7628
|
onSortChanged: this.onColumnSortChanged.bind(this),
|
|
7590
7629
|
onFilterChanged: this.onColumnFilterChanged.bind(this),
|
|
7591
7630
|
onHideColumn: this.onHideColumn.bind(this),
|
|
7631
|
+
onMenuClosed: this.onColumnMenuClosed.bind(this),
|
|
7592
7632
|
getSortDirection: this.getColumnSortDirection.bind(this),
|
|
7593
7633
|
getFilterValues: this.getColumnFilterValues.bind(this),
|
|
7594
7634
|
isColumnSorted: this.isColumnSorted.bind(this),
|
|
@@ -7636,12 +7676,16 @@ class AdsTableComponent {
|
|
|
7636
7676
|
ngOnChanges(changes) {
|
|
7637
7677
|
// When rowData changes (e.g., async load), refresh the column definitions
|
|
7638
7678
|
// so that filterOptions are recalculated from the new data
|
|
7639
|
-
if (changes['rowData'] &&
|
|
7679
|
+
if (changes['rowData'] && this.enableCustomSortFilter) {
|
|
7640
7680
|
const newData = changes['rowData'].currentValue;
|
|
7641
7681
|
const oldData = changes['rowData'].previousValue;
|
|
7642
|
-
//
|
|
7682
|
+
// Invalidate all caches when rowData changes
|
|
7683
|
+
this.invalidateDataCache();
|
|
7684
|
+
// Refresh headers if we went from empty/null to having data
|
|
7643
7685
|
if ((!oldData || oldData.length === 0) && newData && newData.length > 0) {
|
|
7644
|
-
|
|
7686
|
+
Promise.resolve().then(() => {
|
|
7687
|
+
this.refreshColumnHeaders();
|
|
7688
|
+
});
|
|
7645
7689
|
}
|
|
7646
7690
|
}
|
|
7647
7691
|
}
|
|
@@ -7783,16 +7827,17 @@ class AdsTableComponent {
|
|
|
7783
7827
|
}
|
|
7784
7828
|
/** @ignore */
|
|
7785
7829
|
get filterButtonLabel() {
|
|
7786
|
-
|
|
7830
|
+
const filtered = this.filteredColumns();
|
|
7831
|
+
if (filtered.length === 0) {
|
|
7787
7832
|
return 'Filter';
|
|
7788
7833
|
}
|
|
7789
|
-
else if (
|
|
7790
|
-
const column = this.getActiveColumnDefs().find((col) => col.field ===
|
|
7791
|
-
const columnName = column?.headerName || column?.field ||
|
|
7834
|
+
else if (filtered.length === 1) {
|
|
7835
|
+
const column = this.getActiveColumnDefs().find((col) => col.field === filtered[0]);
|
|
7836
|
+
const columnName = column?.headerName || column?.field || filtered[0];
|
|
7792
7837
|
return `Filtered by ${columnName}`;
|
|
7793
7838
|
}
|
|
7794
7839
|
else {
|
|
7795
|
-
const columnNames =
|
|
7840
|
+
const columnNames = filtered.map((fieldName) => {
|
|
7796
7841
|
const column = this.getActiveColumnDefs().find((col) => col.field === fieldName);
|
|
7797
7842
|
return column?.headerName || column?.field || fieldName;
|
|
7798
7843
|
});
|
|
@@ -7805,7 +7850,7 @@ class AdsTableComponent {
|
|
|
7805
7850
|
}
|
|
7806
7851
|
/** @ignore */
|
|
7807
7852
|
get isFilteredTable() {
|
|
7808
|
-
return this.filteredColumns.length > 0;
|
|
7853
|
+
return this.filteredColumns().length > 0;
|
|
7809
7854
|
}
|
|
7810
7855
|
/** @ignore */
|
|
7811
7856
|
updateFilteringState() {
|
|
@@ -7817,28 +7862,30 @@ class AdsTableComponent {
|
|
|
7817
7862
|
}
|
|
7818
7863
|
// Check custom column filter states
|
|
7819
7864
|
for (const [field, values] of this.columnFilterStates()) {
|
|
7820
|
-
|
|
7821
|
-
|
|
7865
|
+
// Check if this column has explicit config in columnSortFilterConfigs
|
|
7866
|
+
const explicitConfig = this.columnSortFilterConfigs.find(c => c.field === field);
|
|
7867
|
+
if (explicitConfig) {
|
|
7868
|
+
// Use the original filterOptions from config if provided, otherwise get unique values
|
|
7869
|
+
const baseFilterOptions = explicitConfig.filterOptions && explicitConfig.filterOptions.length > 0
|
|
7870
|
+
? explicitConfig.filterOptions
|
|
7871
|
+
: this.getUniqueColumnValues(field);
|
|
7822
7872
|
// Column is filtered if:
|
|
7823
7873
|
// - No options are selected (empty array = all unchecked)
|
|
7824
7874
|
// - Some but not all options are selected
|
|
7825
|
-
// - filterOptions is empty but we have stored values (data not loaded yet)
|
|
7826
|
-
const hasFilterOptions = config.filterOptions.length > 0;
|
|
7827
7875
|
const isFiltered = values.length === 0 ||
|
|
7828
|
-
(
|
|
7829
|
-
(!hasFilterOptions && values.length > 0);
|
|
7876
|
+
(baseFilterOptions.length > 0 && values.length < baseFilterOptions.length);
|
|
7830
7877
|
if (isFiltered && !filteredColumnsList.includes(field)) {
|
|
7831
7878
|
filteredColumnsList.push(field);
|
|
7832
7879
|
}
|
|
7833
7880
|
}
|
|
7834
7881
|
else if (values.length > 0) {
|
|
7835
|
-
//
|
|
7882
|
+
// For columns without explicit config, any stored filter state means filter is active
|
|
7836
7883
|
if (!filteredColumnsList.includes(field)) {
|
|
7837
7884
|
filteredColumnsList.push(field);
|
|
7838
7885
|
}
|
|
7839
7886
|
}
|
|
7840
7887
|
}
|
|
7841
|
-
this.filteredColumns
|
|
7888
|
+
this.filteredColumns.set(filteredColumnsList);
|
|
7842
7889
|
}
|
|
7843
7890
|
/** @ignore */
|
|
7844
7891
|
onSortChanged() {
|
|
@@ -7866,16 +7913,23 @@ class AdsTableComponent {
|
|
|
7866
7913
|
// Clear custom column filter states (reset to all options selected)
|
|
7867
7914
|
const newStates = new Map();
|
|
7868
7915
|
for (const [field] of this.columnFilterStates()) {
|
|
7869
|
-
|
|
7916
|
+
// Use base config to get ALL options, not filtered ones
|
|
7917
|
+
const config = this.getBaseColumnSortFilterConfig(field);
|
|
7870
7918
|
if (config?.filterOptions) {
|
|
7871
7919
|
// Reset to all options selected
|
|
7872
7920
|
newStates.set(field, [...config.filterOptions]);
|
|
7873
7921
|
}
|
|
7874
7922
|
}
|
|
7875
7923
|
this.columnFilterStates.set(newStates);
|
|
7924
|
+
// Invalidate the filter cache since filters changed
|
|
7925
|
+
this.invalidateFilterCache();
|
|
7876
7926
|
// Trigger external filter update
|
|
7877
7927
|
this.gridApi.onFilterChanged();
|
|
7878
7928
|
this.updateFilteringState();
|
|
7929
|
+
// Refresh headers to update filter options in menus
|
|
7930
|
+
if (this.enableCustomSortFilter) {
|
|
7931
|
+
this.gridApi.refreshHeader();
|
|
7932
|
+
}
|
|
7879
7933
|
// Emit event to notify the header template that filters were cleared
|
|
7880
7934
|
this.filtersCleared.emit();
|
|
7881
7935
|
}
|
|
@@ -7892,11 +7946,17 @@ class AdsTableComponent {
|
|
|
7892
7946
|
}
|
|
7893
7947
|
}
|
|
7894
7948
|
// ============ Custom Sort/Filter Menu Methods ============
|
|
7895
|
-
/** @ignore - Extract unique values from a column in rowData */
|
|
7949
|
+
/** @ignore - Extract unique values from a column in rowData (with caching) */
|
|
7896
7950
|
getUniqueColumnValues(field) {
|
|
7897
7951
|
if (!this.rowData || this.rowData.length === 0) {
|
|
7898
7952
|
return [];
|
|
7899
7953
|
}
|
|
7954
|
+
// Check cache first
|
|
7955
|
+
const cacheKey = `${field}_v${this.dataVersion}`;
|
|
7956
|
+
const cached = this.uniqueColumnValuesCache.get(cacheKey);
|
|
7957
|
+
if (cached) {
|
|
7958
|
+
return cached;
|
|
7959
|
+
}
|
|
7900
7960
|
const uniqueValues = new Set();
|
|
7901
7961
|
this.rowData.forEach(row => {
|
|
7902
7962
|
const value = row[field];
|
|
@@ -7904,14 +7964,145 @@ class AdsTableComponent {
|
|
|
7904
7964
|
uniqueValues.add(String(value));
|
|
7905
7965
|
}
|
|
7906
7966
|
});
|
|
7907
|
-
|
|
7967
|
+
const result = Array.from(uniqueValues).sort();
|
|
7968
|
+
// Cache the result
|
|
7969
|
+
this.uniqueColumnValuesCache.set(cacheKey, result);
|
|
7970
|
+
return result;
|
|
7971
|
+
}
|
|
7972
|
+
/**
|
|
7973
|
+
* @ignore - Extract unique values for a column, considering filters from OTHER columns only.
|
|
7974
|
+
* This enables cross-filtering: when you filter Status="Delayed", the Destination filter
|
|
7975
|
+
* shows all destinations that have at least one "Delayed" row, and vice versa.
|
|
7976
|
+
* Uses caching to avoid expensive recalculations on every access.
|
|
7977
|
+
*/
|
|
7978
|
+
getFilteredColumnValues(field) {
|
|
7979
|
+
if (!this.rowData || this.rowData.length === 0) {
|
|
7980
|
+
return [];
|
|
7981
|
+
}
|
|
7982
|
+
// Check cache first
|
|
7983
|
+
const cacheKey = `${field}_v${this.filterCacheVersion}`;
|
|
7984
|
+
const cached = this.filteredColumnValuesCache.get(cacheKey);
|
|
7985
|
+
if (cached) {
|
|
7986
|
+
return cached;
|
|
7987
|
+
}
|
|
7988
|
+
// Check if any filter is active (excluding the current field)
|
|
7989
|
+
const hasOtherActiveFilters = this.hasActiveFiltersExcluding(field);
|
|
7990
|
+
// If no other filters are active, return all unique values
|
|
7991
|
+
if (!hasOtherActiveFilters) {
|
|
7992
|
+
const values = this.getUniqueColumnValues(field);
|
|
7993
|
+
this.filteredColumnValuesCache.set(cacheKey, values);
|
|
7994
|
+
return values;
|
|
7995
|
+
}
|
|
7996
|
+
const uniqueValues = new Set();
|
|
7997
|
+
// Iterate through all rows and check if they pass filters from OTHER columns
|
|
7998
|
+
this.rowData.forEach(row => {
|
|
7999
|
+
if (this.doesRowPassFiltersExcluding(row, field)) {
|
|
8000
|
+
const value = row[field];
|
|
8001
|
+
if (value !== null && value !== undefined) {
|
|
8002
|
+
uniqueValues.add(String(value));
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
});
|
|
8006
|
+
// If no values found, fall back to all values
|
|
8007
|
+
let result;
|
|
8008
|
+
if (uniqueValues.size === 0) {
|
|
8009
|
+
result = this.getUniqueColumnValues(field);
|
|
8010
|
+
}
|
|
8011
|
+
else {
|
|
8012
|
+
result = Array.from(uniqueValues).sort();
|
|
8013
|
+
}
|
|
8014
|
+
// Cache the result
|
|
8015
|
+
this.filteredColumnValuesCache.set(cacheKey, result);
|
|
8016
|
+
return result;
|
|
8017
|
+
}
|
|
8018
|
+
/** @ignore - Invalidate the filtered column values cache */
|
|
8019
|
+
invalidateFilterCache() {
|
|
8020
|
+
this.filterCacheVersion++;
|
|
8021
|
+
this.filteredColumnValuesCache.clear();
|
|
8022
|
+
}
|
|
8023
|
+
/** @ignore - Invalidate the unique column values cache (when rowData changes) */
|
|
8024
|
+
invalidateDataCache() {
|
|
8025
|
+
this.dataVersion++;
|
|
8026
|
+
this.uniqueColumnValuesCache.clear();
|
|
8027
|
+
// Also invalidate filter cache since it depends on data
|
|
8028
|
+
this.invalidateFilterCache();
|
|
8029
|
+
}
|
|
8030
|
+
/** @ignore - Check if there are any active filters excluding the specified field */
|
|
8031
|
+
hasActiveFiltersExcluding(excludeField) {
|
|
8032
|
+
for (const [field, selectedValues] of this.columnFilterStates()) {
|
|
8033
|
+
if (field === excludeField)
|
|
8034
|
+
continue;
|
|
8035
|
+
const config = this.getBaseColumnSortFilterConfig(field);
|
|
8036
|
+
if (!config?.filterOptions)
|
|
8037
|
+
continue;
|
|
8038
|
+
// Filter is active if not all options are selected
|
|
8039
|
+
if (selectedValues.length < config.filterOptions.length) {
|
|
8040
|
+
return true;
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
return false;
|
|
8044
|
+
}
|
|
8045
|
+
/** @ignore - Check if a row passes all filters EXCEPT the specified field */
|
|
8046
|
+
doesRowPassFiltersExcluding(row, excludeField) {
|
|
8047
|
+
for (const [field, selectedValues] of this.columnFilterStates()) {
|
|
8048
|
+
if (field === excludeField)
|
|
8049
|
+
continue;
|
|
8050
|
+
const config = this.getBaseColumnSortFilterConfig(field);
|
|
8051
|
+
if (!config?.filterOptions)
|
|
8052
|
+
continue;
|
|
8053
|
+
// If all values are selected, this filter doesn't restrict anything
|
|
8054
|
+
if (selectedValues.length === config.filterOptions.length) {
|
|
8055
|
+
continue;
|
|
8056
|
+
}
|
|
8057
|
+
// If no values are selected, no rows pass
|
|
8058
|
+
if (selectedValues.length === 0) {
|
|
8059
|
+
return false;
|
|
8060
|
+
}
|
|
8061
|
+
const cellValue = row[field];
|
|
8062
|
+
const cellValueStr = cellValue != null ? String(cellValue) : '';
|
|
8063
|
+
if (!selectedValues.includes(cellValueStr)) {
|
|
8064
|
+
return false;
|
|
8065
|
+
}
|
|
8066
|
+
}
|
|
8067
|
+
return true;
|
|
8068
|
+
}
|
|
8069
|
+
/** @ignore - Get base config without dynamic filter options */
|
|
8070
|
+
getBaseColumnSortFilterConfig(field) {
|
|
8071
|
+
const config = this.columnSortFilterConfigs.find(c => c.field === field);
|
|
8072
|
+
// If column is not in columnSortFilterConfigs but has filter state, create a minimal config
|
|
8073
|
+
if (!config) {
|
|
8074
|
+
// Check if this field has filter state (was filtered at some point)
|
|
8075
|
+
if (this.columnFilterStates().has(field)) {
|
|
8076
|
+
return {
|
|
8077
|
+
field,
|
|
8078
|
+
headerName: field,
|
|
8079
|
+
filterOptions: this.getUniqueColumnValues(field),
|
|
8080
|
+
};
|
|
8081
|
+
}
|
|
8082
|
+
return undefined;
|
|
8083
|
+
}
|
|
8084
|
+
// Auto-populate filterOptions from all rowData if not provided
|
|
8085
|
+
if (!config.filterOptions || config.filterOptions.length === 0) {
|
|
8086
|
+
return {
|
|
8087
|
+
...config,
|
|
8088
|
+
filterOptions: this.getUniqueColumnValues(field),
|
|
8089
|
+
};
|
|
8090
|
+
}
|
|
8091
|
+
return config;
|
|
7908
8092
|
}
|
|
7909
8093
|
/** @ignore */
|
|
7910
8094
|
getColumnSortFilterConfig(field) {
|
|
7911
|
-
const config = this.columnSortFilterConfigs.find(
|
|
8095
|
+
const config = this.columnSortFilterConfigs.find(c => c.field === field);
|
|
7912
8096
|
if (!config) {
|
|
7913
8097
|
return undefined;
|
|
7914
8098
|
}
|
|
8099
|
+
// When custom sort/filter is enabled, get values from filtered rows (dynamic filtering)
|
|
8100
|
+
if (this.enableCustomSortFilter && this.gridApi) {
|
|
8101
|
+
return {
|
|
8102
|
+
...config,
|
|
8103
|
+
filterOptions: this.getFilteredColumnValues(field),
|
|
8104
|
+
};
|
|
8105
|
+
}
|
|
7915
8106
|
// Auto-populate filterOptions from rowData if not provided
|
|
7916
8107
|
if (!config.filterOptions || config.filterOptions.length === 0) {
|
|
7917
8108
|
return {
|
|
@@ -7932,7 +8123,8 @@ class AdsTableComponent {
|
|
|
7932
8123
|
return storedValues;
|
|
7933
8124
|
}
|
|
7934
8125
|
// If no filter state exists for this field, return all options (initial state = all selected)
|
|
7935
|
-
|
|
8126
|
+
// Always use base config here to get ALL possible options, not filtered ones
|
|
8127
|
+
const config = this.getBaseColumnSortFilterConfig(field);
|
|
7936
8128
|
return config?.filterOptions ?? [];
|
|
7937
8129
|
}
|
|
7938
8130
|
/** @ignore */
|
|
@@ -7941,7 +8133,8 @@ class AdsTableComponent {
|
|
|
7941
8133
|
}
|
|
7942
8134
|
/** @ignore */
|
|
7943
8135
|
isColumnFiltered(field) {
|
|
7944
|
-
|
|
8136
|
+
// Use base config to get all possible options (not filtered options)
|
|
8137
|
+
const config = this.getBaseColumnSortFilterConfig(field);
|
|
7945
8138
|
if (!config?.filterOptions)
|
|
7946
8139
|
return false;
|
|
7947
8140
|
const selectedValues = this.columnFilterStates().get(field);
|
|
@@ -7998,14 +8191,29 @@ class AdsTableComponent {
|
|
|
7998
8191
|
const newStates = new Map(this.columnFilterStates());
|
|
7999
8192
|
newStates.set(event.field, event.values);
|
|
8000
8193
|
this.columnFilterStates.set(newStates);
|
|
8194
|
+
// Invalidate the filter cache since filters changed
|
|
8195
|
+
this.invalidateFilterCache();
|
|
8001
8196
|
// Apply external filter to grid immediately
|
|
8002
8197
|
if (this.gridApi) {
|
|
8003
8198
|
this.gridApi.onFilterChanged();
|
|
8004
8199
|
this.updateFilteringState();
|
|
8200
|
+
// When custom sort/filter is enabled, mark that headers need refresh
|
|
8201
|
+
// The actual refresh will happen when the menu closes to avoid destroying the open menu
|
|
8202
|
+
if (this.enableCustomSortFilter) {
|
|
8203
|
+
this.pendingHeaderRefresh = true;
|
|
8204
|
+
}
|
|
8005
8205
|
}
|
|
8006
8206
|
// Emit event for external listeners
|
|
8007
8207
|
this.columnFilterChanged.emit(event);
|
|
8008
8208
|
}
|
|
8209
|
+
/** @ignore - Called when a column's sort/filter menu is closed */
|
|
8210
|
+
onColumnMenuClosed() {
|
|
8211
|
+
// If custom sort/filter is enabled and there's a pending refresh, do it now
|
|
8212
|
+
if (this.enableCustomSortFilter && this.pendingHeaderRefresh && this.gridApi) {
|
|
8213
|
+
this.pendingHeaderRefresh = false;
|
|
8214
|
+
this.gridApi.refreshHeader();
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8009
8217
|
/** @ignore */
|
|
8010
8218
|
onHideColumn(field) {
|
|
8011
8219
|
const visibilityItem = this.columnVisibilityList().find(item => item.field === field);
|
|
@@ -8109,11 +8317,7 @@ class AdsTableComponent {
|
|
|
8109
8317
|
params.api.sizeColumnsToFit();
|
|
8110
8318
|
// Re-evaluate filtering state now that data is available
|
|
8111
8319
|
// This ensures filter button shows correct state when filters were applied before data loaded
|
|
8112
|
-
|
|
8113
|
-
setTimeout(() => {
|
|
8114
|
-
this.updateFilteringState();
|
|
8115
|
-
this.cdr.detectChanges();
|
|
8116
|
-
});
|
|
8320
|
+
this.updateFilteringState();
|
|
8117
8321
|
}
|
|
8118
8322
|
/** @ignore */
|
|
8119
8323
|
getAgGridIcons() {
|
|
@@ -8291,12 +8495,32 @@ class AdsTableComponent {
|
|
|
8291
8495
|
? new Map(filterStates)
|
|
8292
8496
|
: new Map(Object.entries(filterStates));
|
|
8293
8497
|
this.columnFilterStates.set(newStates);
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8498
|
+
}
|
|
8499
|
+
/**
|
|
8500
|
+
* Applies the current column filter states to the grid.
|
|
8501
|
+
* Call this method after setting filter states and when data is available to sync filters with grid data.
|
|
8502
|
+
*/
|
|
8503
|
+
applyFiltersToGrid() {
|
|
8504
|
+
if (!this.gridApi) {
|
|
8505
|
+
return;
|
|
8506
|
+
}
|
|
8507
|
+
// Force AG Grid to re-evaluate external filters
|
|
8508
|
+
this.gridApi.onFilterChanged();
|
|
8509
|
+
// Update filtering state for UI (filter button, etc.)
|
|
8510
|
+
this.updateFilteringState();
|
|
8511
|
+
// Refresh headers to update filter button states and menu checkboxes
|
|
8512
|
+
if (this.enableCustomSortFilter) {
|
|
8513
|
+
this.gridApi.refreshHeader();
|
|
8298
8514
|
}
|
|
8299
8515
|
}
|
|
8516
|
+
/**
|
|
8517
|
+
* Reapplies the current column filter states to the grid.
|
|
8518
|
+
* Use this method when data is loaded asynchronously and filters need to be reapplied.
|
|
8519
|
+
* @deprecated Use applyFiltersToGrid() instead
|
|
8520
|
+
*/
|
|
8521
|
+
applyColumnFilters() {
|
|
8522
|
+
this.applyFiltersToGrid();
|
|
8523
|
+
}
|
|
8300
8524
|
/** @ignore */
|
|
8301
8525
|
ngOnDestroy() {
|
|
8302
8526
|
// Clean up the observer when the component is destroyed
|