@progress/kendo-angular-grid
Version:
Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.
293 lines (292 loc) • 13.6 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { MenuTabbingService } from './menu-tabbing.service';
import { Component, Input, SkipSelf, Output, EventEmitter, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { ColumnComponent } from "../../columns/column.component";
import { FilterService } from "../filter.service";
import { removeFilter, filtersByField } from "../base-filter-cell.component";
import { isPresent, isNullOrEmptyString } from "../../utils";
import { cloneFilters } from '../../common/filter-descriptor-differ';
import { ContextService } from '../../common/provider.service';
import { FilterMenuHostDirective } from './filter-menu-host.directive';
import { NgSwitch, NgSwitchCase, NgIf, NgTemplateOutlet, NgClass } from '@angular/common';
import { FormsModule } from '@angular/forms';
import * as i0 from "@angular/core";
import * as i1 from "../filter.service";
import * as i2 from "../../common/provider.service";
import * as i3 from "./menu-tabbing.service";
import * as i4 from "@angular/forms";
const isNoValueOperator = operator => (operator === "isnull"
|| operator === "isnotnull"
|| operator === "isempty"
|| operator === "isnotempty");
const validFilters = ({ value, operator }) => !isNullOrEmptyString(value) || isNoValueOperator(operator);
const trimFilters = filter => {
filter.filters = filter.filters.filter(validFilters);
return filter;
};
const findParent = (filters, field, parent) => {
return filters.reduce((acc, filter) => {
if (acc) {
return acc;
}
if (filter.filters) {
return findParent(filter.filters, field, filter);
}
else if (filter.field === field) {
return parent;
}
return acc;
}, undefined);
};
const parentLogicOfDefault = (filter, field, def = "and") => {
const parent = findParent(((filter || {}).filters || []), field);
return isPresent(parent) ? parent.logic : def;
};
/**
* @hidden
*/
export class FilterMenuContainerComponent {
parentService;
childService;
ctx;
cd;
close = new EventEmitter();
/**
* The column with which the filter is associated.
* @type {ColumnComponent}
*/
column;
/**
* @hidden
*/
isLast;
/**
* @hidden
*/
isExpanded;
/**
* @hidden
*/
menuTabbingService;
/**
* The current root filter.
* @type {CompositeFilterDescriptor}
*/
set filter(value) {
this._filter = cloneFilters(value);
}
get filter() {
return this._filter;
}
/**
* @hidden
*/
actionsClass = 'k-actions k-actions-stretched k-actions-horizontal';
get childFilter() {
if (!isPresent(this._childFilter)) {
this._childFilter = {
filters: filtersByField(this.filter, (this.column || {}).field),
logic: parentLogicOfDefault(this.filter, (this.column || {}).field)
};
}
return this._childFilter;
}
resetButton;
filterButton;
_childFilter;
subscription;
_templateContext = {};
_filter;
constructor(parentService, childService, ctx, cd, menuTabbingService) {
this.parentService = parentService;
this.childService = childService;
this.ctx = ctx;
this.cd = cd;
this.menuTabbingService = menuTabbingService;
}
ngOnInit() {
this.subscription = this.childService.changes.subscribe(filter => this._childFilter = filter);
this.subscription.add(this.ctx.localization.changes.subscribe(() => this.cd.markForCheck()));
}
ngAfterViewChecked() {
if (!this.menuTabbingService.isColumnMenu || (this.isLast && this.isExpanded)) {
this.menuTabbingService.lastFocusable = this.resetButton.nativeElement;
}
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
this.menuTabbingService.lastFocusable = undefined;
}
get disabled() {
return !this.childFilter.filters.some(validFilters);
}
get templateContext() {
this._templateContext.column = this.column;
this._templateContext.filter = this.childFilter;
this._templateContext.filterService = this.childService;
this._templateContext["$implicit"] = this.childFilter;
return this._templateContext;
}
get hasTemplate() {
return isPresent(this.column) && isPresent(this.column.filterMenuTemplateRef);
}
submit() {
const filter = trimFilters(this.childFilter);
if (filter.filters.length) {
const root = this.filter || {
filters: [],
logic: "and"
};
removeFilter(root, this.column.field);
root.filters.push(filter);
this.parentService.filter(root);
}
this.close.emit();
return false;
}
reset() {
const root = this.filter || {
filters: [],
logic: "and"
};
removeFilter(root, this.column.field);
this.parentService.filter(root);
this.close.emit();
}
onTab(e, buttonType) {
if (this.menuTabbingService.firstFocusable && (!this.menuTabbingService.isColumnMenu || this.isLast)) {
e.preventDefault();
if (buttonType === 'reset') {
this.menuTabbingService.firstFocusable.focus();
}
else {
this.disabled ? this.menuTabbingService.firstFocusable.focus() : this.resetButton.nativeElement.focus();
}
}
}
get clearText() {
return this.ctx.localization.get("filterClearButton");
}
get filterText() {
return this.ctx.localization.get("filterFilterButton");
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FilterMenuContainerComponent, deps: [{ token: i1.FilterService, skipSelf: true }, { token: i1.FilterService }, { token: i2.ContextService }, { token: i0.ChangeDetectorRef }, { token: i3.MenuTabbingService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: FilterMenuContainerComponent, isStandalone: true, selector: "kendo-grid-filter-menu-container", inputs: { column: "column", isLast: "isLast", isExpanded: "isExpanded", menuTabbingService: "menuTabbingService", filter: "filter", actionsClass: "actionsClass" }, outputs: { close: "close" }, providers: [
FilterService,
MenuTabbingService
], viewQueries: [{ propertyName: "resetButton", first: true, predicate: ["resetButton"], descendants: true }, { propertyName: "filterButton", first: true, predicate: ["filterButton"], descendants: true }], ngImport: i0, template: `
<form (submit)="submit()" (reset)="reset()"
class="k-filter-menu">
<div class="k-filter-menu-container">
<ng-container [ngSwitch]="hasTemplate">
<ng-container *ngSwitchCase="false">
<ng-container
kendoFilterMenuHost
[filterService]="childService"
[column]="column"
[filter]="childFilter"
[menuTabbingService]="menuTabbingService">
</ng-container>
</ng-container>
<ng-container *ngSwitchCase="true">
<ng-template
*ngIf="column.filterMenuTemplateRef"
[ngTemplateOutlet]="column.filterMenuTemplateRef"
[ngTemplateOutletContext]="templateContext"
>
</ng-template>
</ng-container>
</ng-container>
<div [ngClass]="actionsClass">
<button #filterButton
type="submit"
class="k-button k-button-solid-primary k-button-solid k-button-md k-rounded-md k-button-rectangle"
[disabled]="disabled"
(keydown.tab)="onTab($event, 'filter')">{{filterText}}</button>
<button
#resetButton
type="reset"
class="k-button k-button-solid-base k-button-solid k-button-md k-rounded-md k-button-rectangle"
(keydown.tab)="onTab($event, 'reset')">{{clearText}}</button>
</div>
</div>
</form>
`, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: FilterMenuHostDirective, selector: "[kendoFilterMenuHost]", inputs: ["filterService", "menuTabbingService"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FilterMenuContainerComponent, decorators: [{
type: Component,
args: [{
providers: [
FilterService,
MenuTabbingService
],
selector: 'kendo-grid-filter-menu-container',
template: `
<form (submit)="submit()" (reset)="reset()"
class="k-filter-menu">
<div class="k-filter-menu-container">
<ng-container [ngSwitch]="hasTemplate">
<ng-container *ngSwitchCase="false">
<ng-container
kendoFilterMenuHost
[filterService]="childService"
[column]="column"
[filter]="childFilter"
[menuTabbingService]="menuTabbingService">
</ng-container>
</ng-container>
<ng-container *ngSwitchCase="true">
<ng-template
*ngIf="column.filterMenuTemplateRef"
[ngTemplateOutlet]="column.filterMenuTemplateRef"
[ngTemplateOutletContext]="templateContext"
>
</ng-template>
</ng-container>
</ng-container>
<div [ngClass]="actionsClass">
<button #filterButton
type="submit"
class="k-button k-button-solid-primary k-button-solid k-button-md k-rounded-md k-button-rectangle"
[disabled]="disabled"
(keydown.tab)="onTab($event, 'filter')">{{filterText}}</button>
<button
#resetButton
type="reset"
class="k-button k-button-solid-base k-button-solid k-button-md k-rounded-md k-button-rectangle"
(keydown.tab)="onTab($event, 'reset')">{{clearText}}</button>
</div>
</div>
</form>
`,
standalone: true,
imports: [FormsModule, NgSwitch, NgSwitchCase, FilterMenuHostDirective, NgIf, NgTemplateOutlet, NgClass]
}]
}], ctorParameters: function () { return [{ type: i1.FilterService, decorators: [{
type: SkipSelf
}] }, { type: i1.FilterService }, { type: i2.ContextService }, { type: i0.ChangeDetectorRef }, { type: i3.MenuTabbingService }]; }, propDecorators: { close: [{
type: Output
}], column: [{
type: Input
}], isLast: [{
type: Input
}], isExpanded: [{
type: Input
}], menuTabbingService: [{
type: Input
}], filter: [{
type: Input
}], actionsClass: [{
type: Input
}], resetButton: [{
type: ViewChild,
args: ['resetButton', { static: false }]
}], filterButton: [{
type: ViewChild,
args: ['filterButton', { static: false }]
}] } });