UNPKG

@progress/kendo-angular-filter

Version:
330 lines (325 loc) 19 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { ChangeDetectorRef, Component, ElementRef, forwardRef, Input, isDevMode, Renderer2 } from '@angular/core'; import { LocalizationService } from '@progress/kendo-angular-l10n'; import { xIcon } from '@progress/kendo-svg-icons'; import { BaseFilterRowComponent } from './base-filter-row.component'; import { FilterErrorMessages } from './error-messages'; import { FilterService } from './filter.service'; import { NavigationService } from './navigation.service'; import { defaultDateOperators, defaultNumericOperators, defaultOperators, defaultStringOperators, FilterItem, getKeyByValue, isFilterEditor, localizeOperators, nullOperators } from './util'; import { ButtonComponent } from '@progress/kendo-angular-buttons'; import { FilterDateEditorComponent } from './editors/date-editor.component'; import { FilterBooleanEditorComponent } from './editors/boolean-editor.component'; import { FilterNumericEditorComponent } from './editors/numeric-editor.component'; import { FilterTextEditorComponent } from './editors/text-editor.component'; import { FilterExpressionOperatorsComponent } from './filter-expression-operators.component'; import { NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { AriaLabelValueDirective } from './aria-label.directive'; import { DropDownListComponent } from '@progress/kendo-angular-dropdowns'; import { TemplateContextDirective } from '@progress/kendo-angular-common'; import * as i0 from "@angular/core"; import * as i1 from "./filter.service"; import * as i2 from "./navigation.service"; import * as i3 from "@progress/kendo-angular-l10n"; /** * @hidden */ export class FilterExpressionComponent extends BaseFilterRowComponent { filterService; cdr; /** * @hidden */ xIcon = xIcon; static ngAcceptInputType_currentItem; currentItem; operators = []; isBoolean = false; editorType; isEditorDisabled = false; editorTemplate; editorFormat; get currentFilterExpression() { return this.getFilterExpressionByField(this.currentItem.field); } get numericEditorFormat() { const isSupportedFormat = typeof this.editorFormat !== 'string' && !this.isNumberFormat(this.editorFormat); if (this.editorFormat && isSupportedFormat) { console.warn(FilterErrorMessages.improperNumericEditorValue(this.currentFilterExpression.title)); } return this.editorFormat; } get dateEditorFormat() { const isSupportedFormat = typeof this.editorFormat !== 'string' && !this.isDateFormat(this.editorFormat); if (this.editorFormat && isSupportedFormat) { console.warn(FilterErrorMessages.improperDateEditorValue(this.currentFilterExpression.title)); } return this.editorFormat; } isNumberFormat(obj) { if (isDevMode() && obj && (obj['currency'] || obj['currencyDisplay'] || obj['maximumFractionDigits'] || obj['minimumIntegerDigits'] || obj['style'] || obj['useGrouping'])) { return true; } else { return false; } } isDateFormat(obj) { if (isDevMode() && obj && obj['displayFormat'] && obj['inputFormat']) { return true; } else { return false; } } localizationSubscription; constructor(filterService, cdr, element, navigationService, localization, renderer) { super(element, navigationService, localization, renderer); this.filterService = filterService; this.cdr = cdr; } ngOnInit() { this.isEditorDisabled = nullOperators.indexOf(this.currentItem.operator) >= 0; const foundFilter = this.getFilterExpressionByField(this.currentItem.field); if (this.currentItem.field) { this.setOperators(foundFilter); } if (foundFilter?.editorFormat) { this.editorFormat = foundFilter.editorFormat; } const defaultFilter = this.getFilterExpressionByField(this.filterService.filters[0].field); if (!this.currentItem.field) { this.currentItem.field = this.filterService.filters[0].field; if (defaultFilter.editorFormat) { this.editorFormat = defaultFilter.editorFormat; } this.setOperators(defaultFilter); } this.setEditorTemplate(); this.localizationSubscription = this.localization.changes.subscribe(() => { this.setOperators(foundFilter || defaultFilter); this.cdr.detectChanges(); }); } ngOnDestroy() { if (this.localizationSubscription) { this.localizationSubscription.unsubscribe(); } } normalizeOperators(filterEditor, operators) { const result = []; for (let j = 0; j < operators.length; j++) { if (isFilterEditor(filterEditor)) { result.push({ value: operators[j], text: this.localization.get(getKeyByValue(defaultOperators[filterEditor], operators[j])) }); } } return result; } getFilterExpressionByField(name) { const foundFilter = this.filterService.filters.find(filter => filter.field === name); if (foundFilter) { return foundFilter; } return null; } filterValueChange(value) { this.navigationService.currentToolbarItemIndex = this.itemNumber; this.navigationService.currentToolbarItemChildrenIndex = 0; this.currentItem.value = null; this.currentItem.field = value; this.setEditorTemplate(); const foundFilter = this.getFilterExpressionByField(this.currentItem.field); this.setOperators(foundFilter); this.editorFormat = foundFilter.editorFormat; this.valueChange.emit(); } getDefaultOperators(operatorsType) { switch (operatorsType) { case 'string': return localizeOperators(defaultStringOperators)(this.localization); case 'number': return localizeOperators(defaultNumericOperators)(this.localization); case 'date': return localizeOperators(defaultDateOperators)(this.localization); default: break; } } getEditorType() { const filterExpression = this.filterService.filters.find((filterExpression) => filterExpression.field === this.currentItem.field); return filterExpression?.editor; } getFilters() { return this.filterService.filters || []; } removeFilterExpression() { this.filterService.remove(this.currentItem, this.index); this.valueChange.emit(true); } setOperators(filter) { this.isBoolean = filter.editor === 'boolean'; if (this.isBoolean) { return; } if (filter.operators) { const localizedOperators = this.normalizeOperators(filter.editor, filter.operators); this.operators = localizedOperators; } else { this.operators = this.getDefaultOperators(filter.editor); } if (!this.currentItem.operator) { this.currentItem.operator = this.operators[0].value; } const operatorDoesNotExist = !this.operators.find(operator => operator.value === this.currentItem.operator); if (this.currentItem.operator && operatorDoesNotExist) { this.currentItem.operator = this.operators[0].value; } } onOperatorChange(value) { this.navigationService.currentToolbarItemIndex = this.itemNumber; this.navigationService.currentToolbarItemChildrenIndex = 1; this.valueChange.emit(); if (nullOperators.includes(value)) { this.currentItem.value = null; this.isEditorDisabled = true; } else { this.isEditorDisabled = false; } } setEditorTemplate() { const filterExpression = this.filterService.filters.find((filter) => filter.field === this.currentItem.field); this.editorTemplate = filterExpression?.editorTemplate; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FilterExpressionComponent, deps: [{ token: i1.FilterService }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i2.NavigationService }, { token: i3.LocalizationService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: FilterExpressionComponent, isStandalone: true, selector: "kendo-filter-expression", inputs: { currentItem: "currentItem" }, providers: [{ provide: FilterItem, useExisting: forwardRef(() => FilterExpressionComponent) }], usesInheritance: true, ngImport: i0, template: ` <div class="k-filter-toolbar"> <div class="k-toolbar k-toolbar-md k-toolbar-solid" role="toolbar" [attr.aria-label]="messageFor('filterToolbarAriaLabel')" (mousedown)="onMouseDown($event)"> <div class="k-filter-field k-toolbar-item" > <kendo-dropdownlist [tabindex]="-1" [kendoAriaLabelValue]="messageFor('filterFieldAriaLabel')" [title]="messageFor('filterExpressionFilters')" [data]="getFilters()" textField="title" valueField="field" [value]="currentItem.field" [valuePrimitive]="true" (valueChange)="filterValueChange($event)"> </kendo-dropdownlist> </div> <div *ngIf="!isBoolean" class="k-filter-operator k-toolbar-item" > <kendo-filter-expression-operators [currentItem]="currentItem" [operators]="operators" [editorType]="getEditorType()" (valueChange)="onOperatorChange($event);"> </kendo-filter-expression-operators> </div> <div class="k-filter-value k-toolbar-item"> <ng-container *ngIf="!editorTemplate" [ngSwitch]="getEditorType()"> <kendo-filter-text-editor *ngSwitchCase="'string'" [currentItem]="currentItem" [isDisabled]="isEditorDisabled" (valueChange)="valueChange.emit()"></kendo-filter-text-editor> <kendo-filter-numeric-editor *ngSwitchCase="'number'" [currentItem]="currentItem" [isDisabled]="isEditorDisabled" [format]="numericEditorFormat" (valueChange)="valueChange.emit()"></kendo-filter-numeric-editor> <kendo-filter-boolean-editor *ngSwitchCase="'boolean'" [currentItem]="currentItem" (valueChange)="valueChange.emit()"></kendo-filter-boolean-editor> <kendo-filter-date-editor *ngSwitchCase="'date'" [currentItem]="currentItem" [isDisabled]="isEditorDisabled" [format]="dateEditorFormat" (valueChange)="valueChange.emit()"></kendo-filter-date-editor> </ng-container> <ng-container *ngIf="editorTemplate"> <ng-template [templateContext]="{templateRef: editorTemplate, $implicit: currentItem}"> </ng-template> </ng-container> </div> <button kendoButton class="k-toolbar-button" tabindex="-1" icon="x" [svgIcon]="xIcon" fillMode="flat" [title]="messageFor('remove')" (click)="removeFilterExpression()"> </button> </div> </div> `, isInline: true, dependencies: [{ kind: "component", type: DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "directive", type: AriaLabelValueDirective, selector: "[kendoAriaLabelValue]", inputs: ["kendoAriaLabelValue"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FilterExpressionOperatorsComponent, selector: "kendo-filter-expression-operators", inputs: ["currentItem", "editorType", "operators"], outputs: ["valueChange"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: FilterTextEditorComponent, selector: "kendo-filter-text-editor", inputs: ["currentItem", "isDisabled"], outputs: ["valueChange"] }, { kind: "component", type: FilterNumericEditorComponent, selector: "kendo-filter-numeric-editor", inputs: ["currentItem", "isDisabled", "format"], outputs: ["valueChange"] }, { kind: "component", type: FilterBooleanEditorComponent, selector: "kendo-filter-boolean-editor", inputs: ["currentItem"], outputs: ["valueChange"] }, { kind: "component", type: FilterDateEditorComponent, selector: "kendo-filter-date-editor", inputs: ["currentItem", "isDisabled", "format"], outputs: ["valueChange"] }, { kind: "directive", type: TemplateContextDirective, selector: "[templateContext]", inputs: ["templateContext"] }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FilterExpressionComponent, decorators: [{ type: Component, args: [{ providers: [{ provide: FilterItem, useExisting: forwardRef(() => FilterExpressionComponent) }], selector: 'kendo-filter-expression', template: ` <div class="k-filter-toolbar"> <div class="k-toolbar k-toolbar-md k-toolbar-solid" role="toolbar" [attr.aria-label]="messageFor('filterToolbarAriaLabel')" (mousedown)="onMouseDown($event)"> <div class="k-filter-field k-toolbar-item" > <kendo-dropdownlist [tabindex]="-1" [kendoAriaLabelValue]="messageFor('filterFieldAriaLabel')" [title]="messageFor('filterExpressionFilters')" [data]="getFilters()" textField="title" valueField="field" [value]="currentItem.field" [valuePrimitive]="true" (valueChange)="filterValueChange($event)"> </kendo-dropdownlist> </div> <div *ngIf="!isBoolean" class="k-filter-operator k-toolbar-item" > <kendo-filter-expression-operators [currentItem]="currentItem" [operators]="operators" [editorType]="getEditorType()" (valueChange)="onOperatorChange($event);"> </kendo-filter-expression-operators> </div> <div class="k-filter-value k-toolbar-item"> <ng-container *ngIf="!editorTemplate" [ngSwitch]="getEditorType()"> <kendo-filter-text-editor *ngSwitchCase="'string'" [currentItem]="currentItem" [isDisabled]="isEditorDisabled" (valueChange)="valueChange.emit()"></kendo-filter-text-editor> <kendo-filter-numeric-editor *ngSwitchCase="'number'" [currentItem]="currentItem" [isDisabled]="isEditorDisabled" [format]="numericEditorFormat" (valueChange)="valueChange.emit()"></kendo-filter-numeric-editor> <kendo-filter-boolean-editor *ngSwitchCase="'boolean'" [currentItem]="currentItem" (valueChange)="valueChange.emit()"></kendo-filter-boolean-editor> <kendo-filter-date-editor *ngSwitchCase="'date'" [currentItem]="currentItem" [isDisabled]="isEditorDisabled" [format]="dateEditorFormat" (valueChange)="valueChange.emit()"></kendo-filter-date-editor> </ng-container> <ng-container *ngIf="editorTemplate"> <ng-template [templateContext]="{templateRef: editorTemplate, $implicit: currentItem}"> </ng-template> </ng-container> </div> <button kendoButton class="k-toolbar-button" tabindex="-1" icon="x" [svgIcon]="xIcon" fillMode="flat" [title]="messageFor('remove')" (click)="removeFilterExpression()"> </button> </div> </div> `, standalone: true, imports: [DropDownListComponent, AriaLabelValueDirective, NgIf, FilterExpressionOperatorsComponent, NgSwitch, NgSwitchCase, FilterTextEditorComponent, FilterNumericEditorComponent, FilterBooleanEditorComponent, FilterDateEditorComponent, TemplateContextDirective, ButtonComponent] }] }], ctorParameters: function () { return [{ type: i1.FilterService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i2.NavigationService }, { type: i3.LocalizationService }, { type: i0.Renderer2 }]; }, propDecorators: { currentItem: [{ type: Input }] } });