UNPKG

@progress/kendo-angular-dialog

Version:
1,281 lines (1,257 loc) 170 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import * as i0 from '@angular/core'; import { TemplateRef, EventEmitter, Component, Input, Output, HostBinding, InjectionToken, Injectable, Inject, Optional, Directive, forwardRef, ContentChildren, ViewChildren, ViewChild, isDevMode, Renderer2, Host, HostListener, ContentChild, NgModule } from '@angular/core'; import { NgIf, NgFor, NgClass, NgTemplateOutlet, NgStyle } from '@angular/common'; import * as i2 from '@angular/animations'; import { style, animate, keyframes, trigger, state, transition } from '@angular/animations'; import * as i1 from '@progress/kendo-angular-buttons'; import { KENDO_BUTTON, ButtonComponent, Button } from '@progress/kendo-angular-buttons'; import * as i1$1 from '@progress/kendo-angular-l10n'; import { LocalizationService, L10N_PREFIX, RTL, ComponentMessages } from '@progress/kendo-angular-l10n'; import { take, delay, takeUntil, filter, map, share, tap, switchMap } from 'rxjs/operators'; import { xIcon, windowRestoreIcon, windowIcon, windowMinimizeIcon } from '@progress/kendo-svg-icons'; import { validatePackage } from '@progress/kendo-licensing'; import { of, Subscription, Subject, merge } from 'rxjs'; import * as i1$2 from '@progress/kendo-angular-common'; import { setHTMLAttributes, shouldShowValidationUI, isDocumentAvailable, focusableSelector, WatermarkOverlayComponent, DraggableDirective, isChanged } from '@progress/kendo-angular-common'; import { offset, scrollPosition, positionWithScroll, getDocumentElement, getWindowViewPort } from '@progress/kendo-popup-common'; import { IconWrapperComponent, IconsService } from '@progress/kendo-angular-icons'; /** * Specifies the action buttons of the Dialog * ([see example]({% slug actionbuttons_dialog %})). */ class DialogActionsComponent { el; /** * Allows the declarative specification of the actions. */ set actions(value) { if (value instanceof TemplateRef) { this.actionsTemplate = value; } else if (Array.isArray(value)) { this.actionsArray = value; } else { throw new Error('"actions" must be either TemplateRef or DialogAction[] instance.'); } } /** * @hidden */ actionsArray; /** * @hidden */ actionsTemplate; /** * Specifies the possible layout of the action buttons. * @default 'stretched' */ layout = 'stretched'; /** * Fires when the user clicks an action button. */ action = new EventEmitter(); hostClasses = true; get startClassName() { return this.layout === 'start'; } get centerClassName() { return this.layout === 'center'; } get endClassName() { return this.layout === 'end'; } get stretchedClassName() { return this.layout === 'stretched'; } constructor(el) { this.el = el; } /** * @hidden */ onButtonClick(action, _e) { this.action.emit(action); } /** * @hidden */ isDivider(action) { return action === 'spacer'; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DialogActionsComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DialogActionsComponent, isStandalone: true, selector: "kendo-dialog-actions", inputs: { actions: "actions", layout: "layout" }, outputs: { action: "action" }, host: { properties: { "class.k-actions": "this.hostClasses", "class.k-actions-horizontal": "this.hostClasses", "class.k-window-actions": "this.hostClasses", "class.k-dialog-actions": "this.hostClasses", "class.k-actions-start": "this.startClassName", "class.k-actions-center": "this.centerClassName", "class.k-actions-end": "this.endClassName", "class.k-actions-stretched": "this.stretchedClassName" } }, ngImport: i0, template: ` <ng-content *ngIf="!actions"></ng-content> <ng-container *ngIf="actionsArray; else actionTemplate"> <ng-container *ngFor="let action of actionsArray"> <ng-container *ngIf="isDivider(action); else defaultAction"> <span class="k-spacer"></span> </ng-container> <ng-template #defaultAction> <button type="button" kendoButton [disabled]="action.disabled" [fillMode]="action.fillMode" [themeColor]="action.themeColor" [ngClass]="action.cssClass" (click)="onButtonClick(action, $event)" [attr.aria-label]="action.text" [svgIcon]="action.svgIcon" [icon]="action.icon" > {{ action.text }} </button> </ng-template> </ng-container> </ng-container> <ng-template #actionTemplate [ngTemplateOutlet]="actionsTemplate"></ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i1.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: DialogActionsComponent, decorators: [{ type: Component, args: [{ selector: 'kendo-dialog-actions', template: ` <ng-content *ngIf="!actions"></ng-content> <ng-container *ngIf="actionsArray; else actionTemplate"> <ng-container *ngFor="let action of actionsArray"> <ng-container *ngIf="isDivider(action); else defaultAction"> <span class="k-spacer"></span> </ng-container> <ng-template #defaultAction> <button type="button" kendoButton [disabled]="action.disabled" [fillMode]="action.fillMode" [themeColor]="action.themeColor" [ngClass]="action.cssClass" (click)="onButtonClick(action, $event)" [attr.aria-label]="action.text" [svgIcon]="action.svgIcon" [icon]="action.icon" > {{ action.text }} </button> </ng-template> </ng-container> </ng-container> <ng-template #actionTemplate [ngTemplateOutlet]="actionsTemplate"></ng-template> `, standalone: true, imports: [NgIf, NgFor, NgClass, NgTemplateOutlet, KENDO_BUTTON] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { actions: [{ type: Input }], layout: [{ type: Input }], action: [{ type: Output }], hostClasses: [{ type: HostBinding, args: ['class.k-actions'] }, { type: HostBinding, args: ['class.k-actions-horizontal'] }, { type: HostBinding, args: ['class.k-window-actions'] }, { type: HostBinding, args: ['class.k-dialog-actions'] }], startClassName: [{ type: HostBinding, args: ['class.k-actions-start'] }], centerClassName: [{ type: HostBinding, args: ['class.k-actions-center'] }], endClassName: [{ type: HostBinding, args: ['class.k-actions-end'] }], stretchedClassName: [{ type: HostBinding, args: ['class.k-actions-stretched'] }] } }); class PreventableEvent { prevented = false; /** * @hidden */ constructor() { } /** * Prevents the default action for a specified event. * In this way, the source component suppresses the built-in behavior that follows the event. */ preventDefault() { this.prevented = true; } /** * If the event is prevented by any of its subscribers, returns `true`. * * @returns `true` if the default action was prevented. Otherwise, returns `false`. */ isDefaultPrevented() { return this.prevented; } } /** * @hidden */ const DIALOG_LOCALIZATION_SERVICE = new InjectionToken('Dialog LocalizationService'); /** * @hidden */ class TitleBarLocalizationService extends LocalizationService { dialogLocalization; constructor(prefix, messageService, rtl, dialogLocalization) { super(prefix, messageService, rtl); this.dialogLocalization = dialogLocalization; } get(shortKey) { if (this.dialogLocalization) { return this.dialogLocalization.get(shortKey); } return super.get(shortKey); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TitleBarLocalizationService, deps: [{ token: L10N_PREFIX }, { token: i1$1.MessageService, optional: true }, { token: RTL, optional: true }, { token: DIALOG_LOCALIZATION_SERVICE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TitleBarLocalizationService }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TitleBarLocalizationService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [L10N_PREFIX] }] }, { type: i1$1.MessageService, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [RTL] }] }, { type: i1$1.LocalizationService, decorators: [{ type: Optional }, { type: Inject, args: [DIALOG_LOCALIZATION_SERVICE] }] }]; } }); /** * @hidden */ class Messages extends ComponentMessages { /** * The title of the close button. */ closeTitle; /** * The title of the restore button. */ restoreTitle; /** * The title of the maximize button. */ maximizeTitle; /** * The title of the minimize button. */ minimizeTitle; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Messages, deps: null, target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: Messages, selector: "kendo-dialog-messages-base", inputs: { closeTitle: "closeTitle", restoreTitle: "restoreTitle", maximizeTitle: "maximizeTitle", minimizeTitle: "minimizeTitle" }, usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Messages, decorators: [{ type: Directive, args: [{ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'kendo-dialog-messages-base' }] }], propDecorators: { closeTitle: [{ type: Input }], restoreTitle: [{ type: Input }], maximizeTitle: [{ type: Input }], minimizeTitle: [{ type: Input }] } }); /** * @hidden */ class LocalizedMessagesDirective extends Messages { service; constructor(service) { super(); this.service = service; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedMessagesDirective, deps: [{ token: i1$1.LocalizationService }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LocalizedMessagesDirective, isStandalone: true, selector: "\n [kendoDialogLocalizedMessages],\n [kendoWindowLocalizedMessages],\n [kendoDialogTitleBarLocalizedMessages]\n ", providers: [ { provide: Messages, useExisting: forwardRef(() => LocalizedMessagesDirective) } ], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedMessagesDirective, decorators: [{ type: Directive, args: [{ providers: [ { provide: Messages, useExisting: forwardRef(() => LocalizedMessagesDirective) } ], selector: ` [kendoDialogLocalizedMessages], [kendoWindowLocalizedMessages], [kendoDialogTitleBarLocalizedMessages] `, standalone: true }] }], ctorParameters: function () { return [{ type: i1$1.LocalizationService }]; } }); /** * Represents the [Kendo UI DialogTitleBar component for Angular]({% slug api_dialog_dialogtitlebarcomponent %}). * * It is used as part of the Dialog content when the component is created dynamically by using an [Angular service]({% slug service_dialog %}). */ class DialogTitleBarComponent { zone; hostElement; localizationService; /** * Fires when the close button of the title-bar is clicked. */ close = new EventEmitter(); /** * @hidden */ id; /** * @hidden */ closeTitle; get className() { return true; } /** * @hidden */ xIcon = xIcon; constructor(zone, hostElement, localizationService) { this.zone = zone; this.hostElement = hostElement; this.localizationService = localizationService; } get closeButtonTitle() { return this.closeTitle || this.localizationService.get('closeTitle'); } ngAfterViewInit() { this.zone.onStable.pipe(take(1)).subscribe(() => { const element = this.hostElement.nativeElement.querySelector('.k-dialog-title'); element.setAttribute('id', this.id); }); } /** * @hidden */ onCloseClick(e) { e.preventDefault(); const eventArgs = new PreventableEvent(); this.close.emit(eventArgs); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DialogTitleBarComponent, deps: [{ token: i0.NgZone }, { token: i0.ElementRef }, { token: i1$1.LocalizationService, optional: true }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DialogTitleBarComponent, isStandalone: true, selector: "kendo-dialog-titlebar", inputs: { id: "id", closeTitle: "closeTitle" }, outputs: { close: "close" }, host: { properties: { "class.k-window-titlebar": "this.className", "class.k-dialog-titlebar": "this.className" } }, providers: [ TitleBarLocalizationService, { provide: LocalizationService, useExisting: TitleBarLocalizationService }, { provide: L10N_PREFIX, useValue: 'kendo.dialog' } ], ngImport: i0, template: ` <ng-container kendoDialogTitleBarLocalizedMessages i18n-closeTitle="kendo.dialog.closeTitle|The title of the close button" closeTitle="Close" > <span class="k-window-title k-dialog-title"> <ng-content></ng-content> </span> <div class="k-window-titlebar-actions k-dialog-titlebar-actions"> <button kendoButton fillMode="flat" type="button" [attr.title]="closeButtonTitle" [attr.aria-label]="closeButtonTitle" icon="close" [svgIcon]="xIcon" class="k-window-titlebar-action k-dialog-titlebar-action" (click)="onCloseClick($event)" > </button> </div> </ng-container> `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective, selector: "\n [kendoDialogLocalizedMessages],\n [kendoWindowLocalizedMessages],\n [kendoDialogTitleBarLocalizedMessages]\n " }, { 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: DialogTitleBarComponent, decorators: [{ type: Component, args: [{ selector: 'kendo-dialog-titlebar', providers: [ TitleBarLocalizationService, { provide: LocalizationService, useExisting: TitleBarLocalizationService }, { provide: L10N_PREFIX, useValue: 'kendo.dialog' } ], template: ` <ng-container kendoDialogTitleBarLocalizedMessages i18n-closeTitle="kendo.dialog.closeTitle|The title of the close button" closeTitle="Close" > <span class="k-window-title k-dialog-title"> <ng-content></ng-content> </span> <div class="k-window-titlebar-actions k-dialog-titlebar-actions"> <button kendoButton fillMode="flat" type="button" [attr.title]="closeButtonTitle" [attr.aria-label]="closeButtonTitle" icon="close" [svgIcon]="xIcon" class="k-window-titlebar-action k-dialog-titlebar-action" (click)="onCloseClick($event)" > </button> </div> </ng-container> `, standalone: true, imports: [LocalizedMessagesDirective, ButtonComponent] }] }], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i0.ElementRef }, { type: i1$1.LocalizationService, decorators: [{ type: Optional }] }]; }, propDecorators: { close: [{ type: Output }], id: [{ type: Input }], closeTitle: [{ type: Input }], className: [{ type: HostBinding, args: ['class.k-window-titlebar'] }, { type: HostBinding, args: ['class.k-dialog-titlebar'] }] } }); /** * @hidden */ const packageMetadata = { name: '@progress/kendo-angular-dialog', productName: 'Kendo UI for Angular', productCode: 'KENDOUIANGULAR', productCodes: ['KENDOUIANGULAR'], publishDate: 1749540027, version: '19.1.1', licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/' }; /** * @hidden */ const isPresent = (value) => value !== null && value !== undefined; /** * @hidden */ const isTruthy = (value) => !!value; const toClassList = (classNames) => String(classNames).trim().split(' '); const focusableRegex = /^(?:a|input|select|textarea|button|object)$/i; /** * @hidden */ var Keys; (function (Keys) { Keys[Keys["esc"] = 27] = "esc"; Keys[Keys["tab"] = 9] = "tab"; Keys[Keys["enter"] = 13] = "enter"; Keys[Keys["space"] = 32] = "space"; Keys[Keys["ctrl"] = 17] = "ctrl"; Keys[Keys["shift"] = 16] = "shift"; Keys[Keys["left"] = 37] = "left"; Keys[Keys["up"] = 38] = "up"; Keys[Keys["right"] = 39] = "right"; Keys[Keys["down"] = 40] = "down"; })(Keys || (Keys = {})); /** * @hidden */ const DIALOG_ELEMENTS_HANDLING_ESC_KEY = 'k-dialog-wrapper k-actions k-dialog-titlebar-action'; /** * @hidden */ const DIALOG_ELEMENTS_HANDLING_ARROWS = 'k-actions'; /** * @hidden */ const WINDOW_CLASSES = 'k-window'; /** * @hidden */ const WINDOW_ELEMENTS_HANDLING_ESC_KEY = 'k-window k-window-titlebar-action'; /** * @hidden */ const hasClasses = (element, classNames) => { const namesList = toClassList(classNames); return Boolean(toClassList(element.className).find((className) => namesList.indexOf(className) >= 0)); }; /** * @hidden */ const isVisible = (element) => { const rect = element.getBoundingClientRect(); return !!(rect.width && rect.height) && window.getComputedStyle(element).visibility !== 'hidden'; }; /** * @hidden */ const isFocusable = (element, checkVisibility = true) => { if (element.tagName) { const tagName = element.tagName.toLowerCase(); const tabIndex = element.getAttribute('tabIndex'); const validTabIndex = tabIndex !== null && !isNaN(tabIndex) && tabIndex > -1; let focusable = false; if (focusableRegex.test(tagName)) { focusable = !element.disabled; } else { focusable = validTabIndex; } return focusable && (!checkVisibility || isVisible(element)); } return false; }; /** * Receives CSS class declarations either as an object, string or array and returns an array of the class names. * * @hidden */ const parseCSSClassNames = (value) => { if (isObject(value)) { return parseObjectClassNames(value); } if (isString(value)) { return parseStringClassNames(value); } if (Array.isArray(value)) { return parseArrayClassNames(value); } }; const parseObjectClassNames = (value) => { const classes = []; Object.keys(value).forEach(className => { const currentClassName = splitStringToArray(className); if (value[className] && currentClassName[0]) { classes.push(...currentClassName); } }); return classes; }; const parseStringClassNames = (value) => { const classes = []; const classesArray = splitStringToArray(value); classesArray.forEach(className => { classes.push(className); }); return classes; }; const parseArrayClassNames = (value) => { const classes = []; value.forEach((className) => { const current = splitStringToArray(className); if (current[0]) { classes.push(...current); } }); return classes; }; /** * @hidden */ const preventDefault = ({ originalEvent: event }) => { event.stopPropagation(); event.preventDefault(); }; /** * @hidden */ const isWindowAvailable = () => { return typeof window !== 'undefined'; }; /** * @hidden */ const preventOnDblClick = release => (mouseDown) => of(mouseDown).pipe(delay(150), takeUntil(release)); /** * @hidden */ const RESIZE_DIRECTIONS = ['n', 'e', 's', 'w', 'se', 'sw', 'ne', 'nw']; /** * @hidden */ const OFFSET_STYLES = ['top', 'left', 'width', 'height']; /** * @hidden */ const isString = (value) => value instanceof String || typeof value === 'string'; /** * @hidden */ const isObject = (value) => isPresent(value) && !Array.isArray(value) && typeof value === 'object'; /** * @hidden */ const isNumber = (value) => typeof value === 'number' && isFinite(value); /** * @hidden */ const createValueWithUnit = (value) => value + (isNumber(value) ? 'px' : ''); /** * @hidden */ const splitStringToArray = (value) => value.trim().replace(/\s+/g, " ").split(' '); /** * @hidden */ const findPrimaryButton = (buttons) => { for (let i = buttons.length - 1; i >= 0; i--) { const classList = buttons[i].classList; for (let j = 0; j < classList.length; j++) { if (classList[j].endsWith('-primary')) { return buttons[i]; } } } }; /** * Indicates that the **Close** button is clicked. Used when the results from * the Dialogs that are opened through `DialogService` are filtered * ([see example]({% slug api_dialog_dialogservice %}#toc-open)). */ class DialogCloseResult { } /** * @hidden */ function animations(duration, direction, animationType) { switch (animationType) { case 'slide': { const translate = direction === 'left' || direction === 'right' ? 'translateX' : 'translateY'; const start = direction === 'right' || direction === 'down' ? -100 : 100; const end = 0; return [ style({ transform: `${translate}(${start}%)` }), animate(`${duration}ms ease-in`, style({ transform: `${translate}(${end}%)` })) ]; } case 'expand': { const scale = direction === 'up' || direction === 'down' ? 'scaleY' : 'scaleX'; const startScale = 0; const endScale = 1; let origin; if (direction === 'down') { origin = 'top'; } else if (direction === 'left') { origin = 'right'; } else if (direction === 'right') { origin = 'left'; } else { origin = 'bottom'; } return [ style({ transform: `${scale}(${startScale})`, transformOrigin: origin }), animate(`${duration}ms ease-in`, style({ transform: `${scale}(${endScale})` })) ]; } case 'zoom': { const startZoom = 0; const endZoom = 1; return [ animate(duration, keyframes([ style({ transform: `scale(${startZoom})` }), style({ transform: `scale(${endZoom})` }) ])) ]; } case 'fade': { const startFade = 0; const endFade = 1; return [ animate(duration, keyframes([ style({ opacity: `${startFade}` }), style({ opacity: `${endFade}` }) ])) ]; } case 'translate': return [ style({ transform: 'translate(0, -10%)' }), animate(`${duration}ms cubic-bezier(.2, 1, .2, 1)`) ]; default: return [ style({ transform: 'translate(0, -10%)' }), animate(`${duration}ms cubic-bezier(.2, 1, .2, 1)`) ]; } } /** * @hidden */ const createPlayer = (builder, animation, animatedElement) => { const factory = builder.build(animation); let player = factory.create(animatedElement); player.onDone(() => { if (player) { player.destroy(); player = null; } }); return player; }; /** * @hidden */ const animateContent = (animation, defAnimationConfig, animatedElement, builder) => { let animationConfig = defAnimationConfig; if (typeof animation !== 'boolean') { animationConfig = animation; animationConfig.duration = animationConfig.duration ? animationConfig.duration : defAnimationConfig.duration; } const animationSpecs = animations(animationConfig.duration, animationConfig.direction, animationConfig.type); const player = createPlayer(builder, animationSpecs, animatedElement); player.play(); }; const DEFAULT_ANIMATION_CONFIG = { duration: 300, type: 'translate' }; /** * Represents the [Kendo UI Dialog component for Angular]({% slug overview_dialog_dialogs %}). */ class DialogComponent { wrapper; renderer; cdr; ngZone; builder; /** * Specifies the action buttons that will be rendered. */ actions; /** * Specifies the layout of the action buttons in the Dialog. * This option is only applicable if the action buttons are specified through the `actions` options. * * @default 'stretched' */ actionsLayout = 'stretched'; /** * Specifies the query selector used to set the initial focus ([see examples]({% slug initial_focus_dialog %})). */ autoFocusedElement; /** * Specifies the text that is rendered in the title bar. */ title; /** * Specifies the width of the Dialog. * A numeric value sets the width in pixels. * A string value sets the width in arbitrary units&mdash;for example, `50%`. */ width; /** * Specifies the minimum width of the Dialog. * A numeric value sets the minimum width in pixels. * A string value sets the minimum width in arbitrary units&mdash;for example, `50%`. */ minWidth; /** * Specifies the maximum width of the Dialog. * A numeric value sets the maximum width in pixels. * A string value sets the maximum width in arbitrary units&mdash;for example, `50%`. */ maxWidth; /** * Specifies the height of the Dialog. * A numeric value sets the height in pixels. * A string value sets the height in arbitrary units&mdash;for example, `50%`. */ height; /** * Specifies the minimum height of the Dialog. * A numeric value sets the minimum height in pixels. * A string value sets the minimum height in arbitrary units&mdash;for example, `50%`. */ minHeight; /** * Specifies the maximum height of the Dialog. * A numeric value sets the maximum height in pixels. * A string value sets the maximum height in arbitrary units&mdash;for example, `50%`. */ maxHeight; /** * Configures the Dialog opening animation ([see example]({% slug animations_dialog %})). * By default the animation type is set to `translate` and its duration is `300ms`. * * @default true */ animation = true; /** * The Dialog allows you to specify predefined theme colors. * The theme color will be applied as a background and border color to the titlebar while also amending the text color accordingly. * * The possible values are: * * `primary` * * `dark` * * `light` */ set themeColor(themeColor) { this.handleThemeColorClass(this.themeColor, themeColor); this._themeColor = themeColor; } get themeColor() { return this._themeColor; } /** * @hidden */ set htmlAttributes(attributes) { setHTMLAttributes(attributes, this.renderer, this.wrapper.nativeElement); const el = this.wrapper.nativeElement; const dir = el.getAttribute('dir'); const tIndex = el.getAttribute('tabindex'); if (this.direction !== dir && dir) { this.direction = dir; } if (this.tabIndex !== tIndex && tIndex) { this.tabIndex = tIndex; } this._htmlAttributes = attributes; } get htmlAttributes() { return this._htmlAttributes; } /** * @hidden */ set cssClass(classes) { this.setServiceClasses(this._cssClass, classes); this._cssClass = classes; } get cssClass() { return this._cssClass; } /** * @hidden */ contentTemplate; /** * @hidden */ titleId = null; /** * @hidden */ contentId = null; /** * @hidden */ closeTitle; /** * @hidden */ showLicenseWatermark = false; /** * Fires when the user clicks an action button of the Dialog. * The event is fired only when the action buttons are specified through the `actions` options. */ action = new EventEmitter(); /** * Fires when the user clicks the **Close** button of the Dialog or the **ESC** key. */ close = new EventEmitter(); get dir() { return this.direction; } tabIndex = 0; titlebarContent; titlebarView; actionsView; dialog; _htmlAttributes; _cssClass; _themeColor = null; direction; subscriptions = []; domSubs = new Subscription(); constructor(wrapper, renderer, localization, cdr, ngZone, builder) { this.wrapper = wrapper; this.renderer = renderer; this.cdr = cdr; this.ngZone = ngZone; this.builder = builder; const isValid = validatePackage(packageMetadata); this.showLicenseWatermark = shouldShowValidationUI(isValid); this.direction = localization.rtl ? 'rtl' : 'ltr'; this.subscriptions.push(localization.changes.subscribe(({ rtl }) => (this.direction = rtl ? 'rtl' : 'ltr'))); this.titleId = this.generateTitleId(); this.contentId = this.generateContentId(); } ngAfterContentInit() { this.bubble('close', this.titlebarContent.first); this.renderer.setAttribute(this.wrapper.nativeElement.querySelector('.k-dialog'), 'aria-describedby', this.contentId); if (this.titlebarContent.first) { this.titlebarContent.first.id = this.titleId; } else { this.subscriptions.push(this.titlebarContent.changes.subscribe(() => { if (isPresent(this.titlebarContent.first)) { this.titlebarContent.first.id = this.titleId; this.ngZone.onStable.pipe(take(1)).subscribe(() => { this.bubble('close', this.titlebarContent.first); this.renderer.setAttribute(this.wrapper.nativeElement.querySelector('.k-dialog'), 'aria-labelledby', this.titleId); }); } })); } } ngAfterViewInit() { if (!isDocumentAvailable()) { return; } this.ngZone.onStable.pipe(take(1)).subscribe(() => { this.handleInitialFocus(); }); this.bubble('close', this.titlebarView.first); this.bubble('action', this.actionsView); if (this.titlebarView.first || this.titlebarContent.first) { //Needed for Dialogs created via service this.renderer.setAttribute(this.wrapper.nativeElement.querySelector('.k-dialog'), 'aria-labelledby', this.titleId); } else { this.subscriptions.push(this.titlebarView.changes.subscribe(() => { if (isPresent(this.titlebarView.first)) { this.titlebarView.first.id = this.titleId; this.ngZone.onStable.pipe(take(1)).subscribe(() => { this.bubble('close', this.titlebarView.first); this.renderer.setAttribute(this.wrapper.nativeElement.querySelector('.k-dialog'), 'aria-labelledby', this.titleId); }); } })); } this.initDomEvents(); this.handleThemeColorClass(null, this.themeColor); } ngOnInit() { if (this.animation) { animateContent(this.animation, DEFAULT_ANIMATION_CONFIG, this.dialog.nativeElement, this.builder); } this.renderer.removeAttribute(this.wrapper.nativeElement, 'title'); this.cdr.detectChanges(); } ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()); this.subscriptions = []; if (this.domSubs) { this.domSubs.unsubscribe(); } } /** * Focuses the wrapper of the Dialog component. */ focus() { const wrapper = this.wrapper.nativeElement; if (isPresent(wrapper)) { wrapper.focus(); } } initDomEvents() { if (!this.wrapper) { return; } this.ngZone.runOutsideAngular(() => { this.domSubs.add(this.renderer.listen(this.wrapper.nativeElement, 'keydown', (ev) => { this.onKeyDown(ev); })); }); } onKeyDown(event) { const target = event.target; const parent = target.parentElement; if (hasClasses(target, DIALOG_ELEMENTS_HANDLING_ESC_KEY) || hasClasses(parent, DIALOG_ELEMENTS_HANDLING_ESC_KEY)) { if (event.keyCode === Keys.esc) { this.ngZone.run(() => { this.close.emit(new DialogCloseResult()); }); } } if (hasClasses(target, 'k-button') && hasClasses(parent, DIALOG_ELEMENTS_HANDLING_ARROWS) && (event.keyCode === Keys.left || event.keyCode === Keys.right)) { this.ngZone.run(() => { this.handleActionButtonFocus(parent, event.keyCode); }); } if (event.keyCode === Keys.tab) { this.ngZone.run(() => { this.keepFocusWithinComponent(target, event); }); } } setServiceClasses(prevValue, value) { const el = this.wrapper.nativeElement; if (prevValue) { parseCSSClassNames(prevValue).forEach(className => { this.renderer.removeClass(el, className); }); } if (value) { parseCSSClassNames(value).forEach(className => { this.renderer.addClass(el, className); }); } } /** * @hidden */ handleInitialFocus() { const wrapper = this.wrapper.nativeElement; const primaryButton = this.findPrimary(wrapper); if (this.autoFocusedElement) { const initiallyFocusedElement = wrapper.querySelector(this.autoFocusedElement); if (initiallyFocusedElement) { initiallyFocusedElement.focus(); } } else if (this.shouldFocusPrimary(primaryButton)) { primaryButton.focus(); } else { wrapper.focus(); } } /** * @hidden */ findPrimary(wrapper) { const actionBtns = wrapper.querySelectorAll('.k-actions .k-button'); return findPrimaryButton(actionBtns); } /** * @hidden */ handleActionButtonFocus(parent, key) { const focusableActionButtons = this.getAllFocusableChildren(parent); for (let i = 0; i < focusableActionButtons.length; i++) { const current = focusableActionButtons[i]; if (current === document.activeElement) { if (key === Keys.left && i > 0) { focusableActionButtons[i - 1].focus(); break; } if (key === Keys.right && i < focusableActionButtons.length - 1) { focusableActionButtons[i + 1].focus(); break; } } } } /** * @hidden */ keepFocusWithinComponent(target, event) { const wrapper = this.wrapper.nativeElement; const [firstFocusable, lastFocusable] = this.getFirstAndLastFocusable(wrapper); const tabAfterLastFocusable = !event.shiftKey && target === lastFocusable; const shiftTabAfterFirstFocusable = event.shiftKey && target === firstFocusable; if (tabAfterLastFocusable) { event.preventDefault(); firstFocusable.focus(); } if (shiftTabAfterFirstFocusable) { event.preventDefault(); lastFocusable.focus(); } } /** * @hidden */ shouldFocusPrimary(el) { return isPresent(el) && isFocusable(el); } /** * @hidden */ getAllFocusableChildren(parent) { return parent.querySelectorAll(focusableSelector); } /** * @hidden */ getFirstAndLastFocusable(parent) { const all = this.getAllFocusableChildren(parent); const firstFocusable = all.length > 0 ? all[0] : parent; const lastFocusable = all.length > 0 ? all[all.length - 1] : parent; return [firstFocusable, lastFocusable]; } /** * @hidden */ generateTitleId() { return 'kendo-dialog-title-' + Math.ceil(Math.random() * 1000000).toString(); } /** * @hidden */ generateContentId() { return 'kendo-dialog-content-' + Math.ceil(Math.random() * 1000000).toString(); } get wrapperClass() { return true; } get styles() { const styles = {}; if (this.width) { styles.width = createValueWithUnit(this.width); } if (this.height) { styles.height = createValueWithUnit(this.height); } if (this.minWidth) { styles.minWidth = createValueWithUnit(this.minWidth); } if (this.maxWidth) { styles.maxWidth = createValueWithUnit(this.maxWidth); } if (this.minHeight) { styles.minHeight = createValueWithUnit(this.minHeight); } if (this.maxHeight) { styles.maxHeight = createValueWithUnit(this.maxHeight); } return styles; } bubble(eventName, component) { if (component) { const emit = e => this[eventName].emit(e); const s = component[eventName].subscribe(emit); this.subscriptions.push(s); } } handleThemeColorClass(previousValue, currentValue) { this.ngZone.onStable.pipe(take(1)).subscribe(() => { const dialog = this.dialog.nativeElement; if (previousValue) { const classToRemove = `k-dialog-${previousValue}`; this.renderer.removeClass(dialog, classToRemove); } if (currentValue) { const classToAdd = `k-dialog-${currentValue}`; this.renderer.addClass(dialog, classToAdd); } }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DialogComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1$1.LocalizationService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i2.AnimationBuilder }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DialogComponent, isStandalone: true, selector: "kendo-dialog", inputs: { actions: "actions", actionsLayout: "actionsLayout", autoFocusedElement: "autoFocusedElement", title: "title", width: "width", minWidth: "minWidth", maxWidth: "maxWidth", height: "height", minHeight: "minHeight", maxHeight: "maxHeight", animation: "animation", themeColor: "themeColor" }, outputs: { action: "action", close: "close" }, host: { properties: { "attr.dir": "this.dir", "attr.tabIndex": "this.tabIndex", "class.k-dialog-wrapper": "this.wrapperClass" } }, providers: [ LocalizationService, { provide: DIALOG_LOCALIZATION_SERVICE, useExisting: LocalizationService }, { provide: L10N_PREFIX, useValue: 'kendo.dialog' } ], queries: [{ propertyName: "titlebarContent", predicate: DialogTitleBarComponent }], viewQueries: [{ propertyName: "actionsView", first: true, predicate: DialogActionsComponent, descendants: true }, { propertyName: "dialog", first: true, predicate: ["dialog"], descendants: true, static: true }, { propertyName: "titlebarView", predicate: DialogTitleBarComponent, descendants: true }], exportAs: ["kendoDialog"], ngImport: i0, template: ` <ng-container kendoDialogLocalizedMessages i18n-closeTitle="kendo.dialog.closeTitle|The title of the close button" closeTitle="Close" > <div class="k-overlay" @overlayAppear></div> <div #dialog class="k-window k-dialog" role="dialog" aria-modal="true" [ngStyle]="styles"> <kendo-dialog-titlebar *ngIf="title" [closeTitle]="closeTitle" [id]="titleId">{{ title }}</kendo-dialog-titlebar> <ng-content select="kendo-dialog-titlebar" *ngIf="!title"></ng-content> <div [id]="contentId" class="k-window-content k-dialog-content"> <ng-content *ngIf="!contentTemplate"></ng-content> <ng-template [ngTemplateOutlet]="contentTemplate" *ngIf="contentTemplate"></ng-template> </div> <ng-content select="kendo-dialog-actions" *ngIf="!actions"></ng-content> <kendo-dialog-actions *ngIf="actions" [actions]="actions" [layout]="actionsLayout"> </kendo-dialog-actions> <div kendoWatermarkOverlay *ngIf="showLicenseWatermark"></div> </div> </ng-container> `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective, selector: "\n [kendoDialogLocalizedMessages],\n [kendoWindowLocalizedMessages],\n [kendoDialogTitleBarLocalizedMessages]\n " }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DialogTitleBarComponent, selector: "kendo-dialog-titlebar", inputs: ["id", "closeTitle"], outputs: ["close"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DialogActionsComponent, selector: "kendo-dialog-actions", inputs: ["actions", "layout"], outputs: ["action"] }, { kind: "component", type: WatermarkOverlayComponent, selector: "div[kendoWatermarkOverlay]" }], animations: [ trigger('overlayAppear', [ state('in', style({ opacity: 1 })), transition('void => *', [style({ opacity: 0.1 }), animate('.3s cubic-bezier(.2, .6, .4, 1)')]) ]) ] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DialogComponent, decorators: [{ type: Component, args: [{ animations: [ trigger('overlayAppear', [ state('in', style({ opacity: 1 })), transition('void => *', [style({ opacity: 0.1 }), animate('.3s cubic-bezier(.2, .6, .4, 1)')]) ]) ], exportAs: 'kendoDialog', providers: [ LocalizationService, { provide: DIALOG_LOCALIZATION_SERVICE, useExisting: LocalizationService }, { provide: L10N_PREFIX, useValue: 'kendo.dialog' } ], selector: 'kendo-dialog', template: ` <ng-container kendoDialogLocalizedMessages i18n-closeTitle="kendo.dialog.closeTitle|The title of the close button" closeTitle="Close" > <div class="k-overlay" @overlayAppear></div> <div #dialog class="k-window k-dialog" role="dialog" aria-modal="true" [ngStyle]="styles"> <kendo-dialog-titlebar *ngIf="title" [closeTitle]="closeTitle" [id]="titleId">{{ title }}</kendo-dialog-titlebar> <ng-content select="kendo-dialog-titlebar" *ngIf="!title"></ng-content> <div [id]="contentId" class="k-window-content k-dialog-content"> <ng-content *ngIf="!contentTemplate"></ng-content> <ng-template [ngTemplateOutlet]="contentTemplate" *ngIf="contentTemplate"></ng-template> </div> <ng-content select="kendo-dialog-actions" *ngIf="!actions"></ng-content> <kendo-dialog-actions *ngIf="actions" [actions]="actions" [layout]="actionsLayout"> </kendo-dialog-actions> <div kendoWatermarkOverlay *ngIf="showLicenseWatermark"></div> </div> </ng-container> `, standalone: true, imports: [LocalizedMessa