@progress/kendo-angular-grid
Version:
Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.
150 lines (149 loc) • 5.9 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 { PopupService } from '@progress/kendo-angular-popup';
import { Injectable, Renderer2, NgZone } from '@angular/core';
import { isPresent } from '../utils';
import { PreventableEvent } from './preventable-event';
import { Subject, Subscription } from 'rxjs';
import { ScrollSyncService } from '../scrolling/scroll-sync.service';
import { isDocumentAvailable } from '@progress/kendo-angular-common';
import { ContextService } from './provider.service';
import { skip } from 'rxjs/operators';
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-popup";
import * as i2 from "../scrolling/scroll-sync.service";
import * as i3 from "./provider.service";
const contains = (node, predicate) => {
while (node) {
if (predicate(node)) {
return true;
}
node = node.parentNode;
}
return false;
};
/**
* Arguments for the `close` event of the filter and column-menu popup.
*/
export class PopupCloseEvent extends PreventableEvent {
/**
* The original DOM event that causes the popup to close.
*/
originalEvent;
constructor(e) {
super();
this.originalEvent = e;
}
}
const DEFAULT_POPUP_CLASS = 'k-grid-filter-popup';
/**
* The service that is used for the popups of the filter and column menus
* ([see example]({% slug filter_menu %}#toc-filter-menu-with-popup)).
*/
export class SinglePopupService {
popupService;
renderer;
ngZone;
ctx;
/**
* Fires when the filter or column menus are about to close because the user clicked outside their popups.
* Used to prevent the popup from closing.
*/
onClose = new Subject();
removeClick;
pointerEventsSub = new Subscription();
popupRef;
scrollSubscription;
canClosePopup = true;
/**
* @hidden
*/
constructor(popupService, renderer, ngZone, scrollSyncService, ctx) {
this.popupService = popupService;
this.renderer = renderer;
this.ngZone = ngZone;
this.ctx = ctx;
this.scrollSubscription = scrollSyncService.changes.pipe(skip(1)).subscribe(() => this.destroy());
}
/**
* @hidden
*/
open(anchor, template, popupRef, popupClass = DEFAULT_POPUP_CLASS) {
const toggle = isPresent(popupRef) && this.popupRef === popupRef;
this.destroy();
if (!toggle) {
const direction = this.ctx.localization.rtl ? 'right' : 'left';
this.popupRef = this.popupService.open({
anchorAlign: { vertical: 'bottom', horizontal: direction },
popupAlign: { vertical: 'top', horizontal: direction },
anchor: anchor,
popupClass: popupClass,
content: template,
positionMode: "absolute"
});
this.renderer.setAttribute(this.popupRef.popupElement, 'dir', this.ctx.localization.rtl ? 'rtl' : 'ltr');
this.attachClose(anchor);
}
const popupEl = this.popupRef?.popupElement;
if (popupEl) {
this.attachMouseListeners(popupEl);
}
return this.popupRef;
}
/**
* @hidden
*/
destroy() {
if (this.popupRef) {
this.detachClose();
this.pointerEventsSub.unsubscribe();
this.pointerEventsSub = null;
this.popupRef.close();
this.popupRef = null;
}
}
ngOnDestroy() {
this.destroy();
this.scrollSubscription.unsubscribe();
}
detachClose() {
if (this.removeClick) {
this.removeClick();
}
}
attachClose(skipElement) {
if (!isDocumentAvailable()) {
return;
}
this.detachClose();
this.ngZone.runOutsideAngular(() => this.removeClick = this.renderer.listen('document', 'click', (e) => {
if (!contains(e.target, x => this.popupRef.popupElement === x || x === skipElement)) {
const args = new PopupCloseEvent(e);
this.onClose.next(args);
if (!args.isDefaultPrevented() && this.canClosePopup) {
this.destroy();
}
this.canClosePopup = true;
}
}));
}
attachMouseListeners(el) {
this.pointerEventsSub = new Subscription();
this.ngZone.runOutsideAngular(() => {
this.pointerEventsSub.add(this.renderer.listen(el, 'pointerdown', (e) => {
e.stopImmediatePropagation();
this.canClosePopup = false;
}));
this.pointerEventsSub.add(this.renderer.listen(el, 'pointerup', () => {
this.canClosePopup = true;
}));
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SinglePopupService, deps: [{ token: i1.PopupService }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i2.ScrollSyncService }, { token: i3.ContextService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SinglePopupService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SinglePopupService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i1.PopupService }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i2.ScrollSyncService }, { type: i3.ContextService }]; } });