@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
1,324 lines (1,308 loc) • 557 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 * as i0 from '@angular/core';
import { Injectable, Directive, Optional, isDevMode, Component, SkipSelf, Host, Input, ViewChild, HostBinding, ViewChildren, ContentChildren, EventEmitter, Output, ContentChild, HostListener, Inject, QueryList, NgZone, forwardRef, ElementRef, ViewEncapsulation, TemplateRef, NgModule } from '@angular/core';
import * as i1 from '@progress/kendo-angular-l10n';
import { LocalizationService, L10N_PREFIX, ComponentMessages } from '@progress/kendo-angular-l10n';
import * as i1$1 from '@progress/kendo-angular-common';
import { Keys, shouldShowValidationUI, WatermarkOverlayComponent, isDocumentAvailable, anyChanged, isObjectPresent, removeHTMLAttributes, parseAttributes, setHTMLAttributes, DraggableDirective, PreventableEvent as PreventableEvent$1, guid, ResizeSensorComponent, hasObservers, isPresent as isPresent$1, focusableSelector, isChanged } from '@progress/kendo-angular-common';
import { validatePackage } from '@progress/kendo-licensing';
import * as i1$2 from '@angular/animations';
import { trigger, state, style, transition, animate, AUTO_STYLE } from '@angular/animations';
import { Subject, BehaviorSubject, Subscription, of } from 'rxjs';
import { chevronUpIcon, chevronDownIcon, caretAltLeftIcon, caretAltRightIcon, caretAltUpIcon, caretAltDownIcon, xIcon, checkCircleIcon, exclamationCircleIcon, chevronRightIcon } from '@progress/kendo-svg-icons';
import { IconWrapperComponent } from '@progress/kendo-angular-icons';
import { NgIf, NgTemplateOutlet, NgFor, NgStyle, NgClass } from '@angular/common';
import { delay, takeUntil, map, tap, filter, switchMap, take } from 'rxjs/operators';
import { ProgressBarComponent } from '@progress/kendo-angular-progressbar';
import { Draggable } from '@progress/kendo-draggable';
import { ButtonComponent } from '@progress/kendo-angular-buttons';
import { DatePipe } from '@progress/kendo-angular-intl';
/**
* @hidden
*/
const packageMetadata = {
name: '@progress/kendo-angular-layout',
productName: 'Kendo UI for Angular',
productCode: 'KENDOUIANGULAR',
productCodes: ['KENDOUIANGULAR'],
publishDate: 1749540075,
version: '19.1.1',
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/?utm_medium=product&utm_source=kendoangular&utm_campaign=kendo-ui-angular-purchase-license-keys-warning'
};
/**
* Represents the expand modes of the PanelBar.
* By default, the expand mode is set to `multiple`.
*/
var PanelBarExpandMode;
(function (PanelBarExpandMode) {
/**
* Allows you to expand only one item at a time.
* When you expand an item, the item that was previously expanded is coll.
*/
PanelBarExpandMode[PanelBarExpandMode["Single"] = 0] = "Single";
/**
* Allows you to expand only one item at a time and requires you to set the `height` property.
* The expanded area occupies the entire height of the PanelBar.
*/
PanelBarExpandMode[PanelBarExpandMode["Full"] = 1] = "Full";
/**
* The default mode of the PanelBar.
* Allows you to expand more than one item at a time. Items can also be toggled.
*/
PanelBarExpandMode[PanelBarExpandMode["Multiple"] = 2] = "Multiple";
/**
* By default, the expand mode is set to `multiple`.
*/
PanelBarExpandMode[PanelBarExpandMode["Default"] = 2] = "Default";
})(PanelBarExpandMode || (PanelBarExpandMode = {}));
/**
* @hidden
*/
let nextPanelbarId = 0;
/**
* @hidden
*/
class PanelBarService {
children$;
keepContent$;
parent$;
pbId;
animate;
expandMode;
itemClick;
childSource;
keepContentSource;
parentSource;
onKeepContent(keepContent) {
this.keepContentSource.next(keepContent);
}
onSelect(event) {
this.childSource.next(event);
}
onFocus() {
this.parentSource.next(true);
}
onBlur() {
this.parentSource.next(false);
}
constructor() {
this.parentSource = new Subject();
this.keepContentSource = new BehaviorSubject(false);
this.childSource = new Subject();
this.itemClick = new Subject();
this.parent$ = this.parentSource.asObservable();
this.children$ = this.childSource.asObservable();
this.keepContent$ = this.keepContentSource.asObservable();
this.pbId = nextPanelbarId++;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarService, decorators: [{
type: Injectable
}], ctorParameters: function () { return []; } });
/**
* Represents the content template of the declaratively initialized PanelBar items.
* The content can be expanded or collapsed through the item.
*/
class PanelBarContentDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarContentDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PanelBarContentDirective, isStandalone: true, selector: "[kendoPanelBarContent]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarContentDirective, decorators: [{
type: Directive,
args: [{
selector: "[kendoPanelBarContent]",
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
/**
* Represents the template directive of the PanelBar which helps to customize the item title
* ([more information and example]({% slug templates_panelbar %}#toc-customizing-the-appearance-of-the-title)).
*
* > The `kendoPanelBarItemTitle` directive overrides the PanelBarItem [title]({% slug api_layout_panelbaritemcomponent %}#toc-title) option.
*
* @example
* ```ts-preview
*
* _@Component({
* selector: 'my-app',
* template: `
* <kendo-panelbar>
* <kendo-panelbar-item [expanded]="true">
* <ng-template kendoPanelBarItemTitle>
* Item Title
* </ng-template>
* </kendo-panelbar-item>
* </kendo-panelbar>
* `
* })
*
* class AppComponent {}
*
* ```
*/
class PanelBarItemTitleDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarItemTitleDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PanelBarItemTitleDirective, isStandalone: true, selector: "[kendoPanelBarItemTitle]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarItemTitleDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPanelBarItemTitle]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }]; } });
let nextId$1 = 0;
const SIZES = {
small: 'sm',
medium: 'md',
large: 'lg'
};
const ROUNDNESS = {
small: 'sm',
medium: 'md',
large: 'lg',
full: 'full'
};
const SHAPE_TO_ROUNDED = {
rounded: 'large',
circle: 'full'
};
/**
* @hidden
*/
const parsePanelBarItems = (data) => {
return data.map((item) => {
if (!isPresent(item.id)) {
item.id = `default-${nextId$1++}`;
}
if (item.children) {
item.children = parsePanelBarItems(item.children);
}
return item;
});
};
/**
* @hidden
*/
const isPresent = (value) => value !== null && value !== undefined;
/**
* @hidden
*/
const isHorizontalArrowKey = keyCode => keyCode === Keys.ArrowLeft || keyCode === Keys.ArrowRight;
/**
* @hidden
*/
const isVerticalArrowKey = keyCode => keyCode === Keys.ArrowUp || keyCode === Keys.ArrowDown;
/**
* @hidden
*/
const isArrowKey = keyCode => isHorizontalArrowKey(keyCode) || isVerticalArrowKey(keyCode);
/**
* @hidden
*/
const isNavigationKey = keyCode => keyCode === Keys.PageUp || keyCode === Keys.PageDown ||
keyCode === Keys.Home || keyCode === Keys.End;
/**
* @hidden
*
* Returns the styling classes to be added and removed
*/
const getStylingClasses = (componentType, stylingOption, previousValue, newValue) => {
switch (stylingOption) {
case 'size':
return {
toRemove: `k-${componentType}-${SIZES[previousValue]}`,
toAdd: newValue !== 'none' ? `k-${componentType}-${SIZES[newValue]}` : ''
};
case 'rounded':
return {
toRemove: `k-rounded-${ROUNDNESS[previousValue]}`,
toAdd: newValue !== 'none' ? `k-rounded-${ROUNDNESS[newValue]}` : ''
};
default:
break;
}
};
/**
* @hidden
*/
const mapShapeToRounded = (shape) => SHAPE_TO_ROUNDED[shape] || 'none';
/**
* @hidden
*/
const isNumber = (value) => typeof value === 'number' && isFinite(value);
const focusableRegex = /^(?:a|input|select|option|textarea|button|object)$/i;
const toClassList = (classNames) => String(classNames).trim().split(' ');
/**
* @hidden
*/
const isFocusable = (element) => {
if (element.tagName) {
const tagName = element.tagName.toLowerCase();
const tabIndex = element.getAttribute('tabindex');
const skipTab = tabIndex === '-1';
let focusable = tabIndex !== null && !skipTab;
if (focusableRegex.test(tagName)) {
focusable = !element.disabled && !skipTab;
}
return focusable;
}
return false;
};
/**
* @hidden
*/
const hasClass = (element, className) => Boolean(toClassList(element.className).find((name) => name === className));
/**
* @hidden
*/
const closestInScope = (target, predicate, scope, targetAttr) => {
while (target && target !== scope && !predicate(target, targetAttr)) {
target = target.parentNode;
}
if (target !== scope) {
return target;
}
};
/**
* @hidden
*/
const itemIndex = (item, indexAttr) => +item.getAttribute(indexAttr);
const hasItemIndex = (item, indexAttr) => isPresent(item.getAttribute(indexAttr));
/**
* @hidden
*/
const closestItem = (target, targetAttr, scope) => closestInScope(target, hasItemIndex, scope, targetAttr);
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* @hidden
*/
let nextId = 0;
/**
* Represents the items of the PanelBar.
*/
class PanelBarItemComponent {
parent;
eventService;
element;
renderer;
/**
* Sets the title of the PanelBar item ([see example]({% slug items_panelbar %}#toc-titles)).
*/
title;
/**
* Allows the component to set the `"id"` property to each item.
* Used to set the `id` attributes of the nested elements and to enable the WAI-ARIA support.
*/
id = `default-${nextId++}`;
/**
* Defines the icon that will be rendered next to the title ([see example]({% slug items_panelbar %}#toc-title-icons)).
*/
icon = '';
/**
* Defines the icon that will be rendered next to the title by using a custom CSS class
* ([see example]({% slug items_panelbar %}#toc-title-icons)).
*/
iconClass = '';
/**
* Defines an SVGIcon to be rendered.
* The input can take either an [existing Kendo SVG icon](slug:svgicon_list) or a custom one.
*/
set svgIcon(icon) {
if (isDevMode() && icon && this.icon && this.iconClass) {
throw new Error('Setting both icon/svgIcon and iconClass options at the same time is not supported.');
}
this._svgIcon = icon;
}
get svgIcon() {
return this._svgIcon;
}
/**
* Defines the location of the image that will be displayed next to the title
* ([see example]({% slug items_panelbar %}#toc-title-images)).
*/
imageUrl = '';
/**
* When set to `true`, disables a PanelBar item ([see example]({% slug items_panelbar %}#toc-disabled-state)).
*/
disabled = false;
/**
* When set to `true`, expands the PanelBar item ([see example]({% slug items_panelbar %}#toc-expanded-state)).
*/
set expanded(value) {
const activeState = this.animate ? "active" : "activeWithoutAnimation";
this.state = value ? activeState : "inactive";
if (!this.keepContent) {
this.toggleExpandedChildAnimations(value);
}
this._expanded = value;
}
get expanded() {
return this._expanded;
}
/**
* Sets the selected state of a PanelBar item ([see example]({% slug items_panelbar %}#toc-selected-state)).
*/
selected = false;
/**
* Sets the content of the PanelBar item.
* By design, it is used when the
* [items]({% slug api_layout_panelbarcomponent %}#toc-items)
* property of the PanelBar is set.
*/
content;
/**
* @hidden
*/
items;
/**
* @hidden
*/
template;
header;
contentWrapper;
contentHeight;
contentOverflow;
keepContent = false;
childrenItems;
hasChildItems = false;
hasItems = false;
hasContent = false;
state = "inactive";
get animate() {
return this.eventService.animate;
}
role = "treeitem";
titleAttribute = null; // eslint-disable-line
kItemClass = true;
get kStateExpandedClass() {
return !this.disabled && this.expanded && (this.hasChildItems || this.hasContent);
}
get itemId() {
return 'k-panelbar-' + this.eventService.pbId + '-item-' + this.id;
}
get ariaExpanded() {
return (this.hasChildItems || this.hasContent) ? !this.disabled && this.expanded : null;
}
get ariaSelected() {
return !this.disabled && this.selected;
}
get ariaDisabled() {
return this.disabled ? true : null;
}
get headerClass() {
return this.parent ? null : true;
}
/**
* @hidden
*/
get titleTemplate() {
return this.titleTemplates.length > 0 ? this.titleTemplates.toArray()[0].templateRef : undefined;
}
viewChildItems;
contentItems;
//ContentChild does not support descendants property, so we use ContentChildren for contentTemplate instead
contentTemplate;
titleTemplates;
focused = false;
wrapperFocused = false;
subscriptions = new Subscription(() => { });
_expanded = false;
level;
_svgIcon;
constructor(parent, eventService, element, renderer) {
this.parent = parent;
this.eventService = eventService;
this.element = element;
this.renderer = renderer;
this.subscriptions.add(eventService.parent$.subscribe(focused => this.onWrapperFocusChange(focused)));
this.subscriptions.add(eventService.keepContent$.subscribe(keepContent => this.keepContent = keepContent));
this.wrapperFocused = parent ? parent.focused : false;
this.level = this.parent ? this.parent.level + 1 : 0;
}
/**
* @hidden
*/
headerHeight() {
return this.element.nativeElement.offsetHeight - (this.contentWrapper ? this.contentWrapper.nativeElement.offsetHeight : 0);
}
ngOnInit() {
this.addLevelClass();
}
ngAfterContentChecked() {
this.hasItems = this.items && this.items.filter(item => !item.hidden).length > 0;
this.hasChildItems = this.contentItems.filter(item => item !== this).length > 0 || this.hasItems;
this.hasContent = (this.contentTemplate !== undefined && this.contentTemplate.length > 0) ||
this.content !== undefined;
this.validateConfiguration();
}
ngAfterViewChecked() {
if (this.items) {
this.childrenItems = this.viewChildItems.toArray();
}
else {
this.childrenItems = this.contentItems.filter(item => item !== this);
}
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
/**
* @hidden
*/
onItemAction() {
if (!this.disabled) {
this.eventService.onSelect(this);
}
}
/**
* @hidden
*/
onItemClick(e) {
if (!isFocusable(e.target)) {
this.eventService.itemClick.next({ item: this.serialize(), originalEvent: e });
this.onItemAction();
}
}
/**
* @hidden
*/
get iconClasses() {
if (this.icon) {
return `${this.icon}`;
}
}
/**
* @hidden
*/
get customIconClasses() {
if (this.iconClass) {
return `${this.iconClass}`;
}
}
/**
* @hidden
*/
get dirInnerCssClasses() {
const dirClass = this.expanded ? 'k-panelbar-collapse' : 'k-panelbar-expand';
return `k-panelbar-toggle ${dirClass}`;
}
/**
* @hidden
*/
get expanderSVGIcon() {
return this.expanded ? chevronUpIcon : chevronDownIcon;
}
/**
* @hidden
*/
serialize() {
return {
content: this.content,
disabled: this.disabled,
expanded: this.expanded,
focused: this.focused,
icon: this.icon,
iconClass: this.iconClass,
svgIcon: this.svgIcon,
id: this.id,
imageUrl: this.imageUrl,
selected: this.selected,
title: this.title,
children: this.items
};
}
/**
* @hidden
*/
subTreeViewItems() {
let subTree = [];
this.viewChildItems.forEach(item => {
subTree = subTree.concat(item.subTreeViewItems());
subTree.push(item);
});
return subTree;
}
/**
* @hidden
*/
validateConfiguration() {
if (isDevMode()) {
if (this.content && (this.contentTemplate !== undefined && this.contentTemplate.length > 0)) {
throw new Error("Invalid configuration: mixed template components and component property.");
}
}
}
/**
* @hidden
*/
toggleAnimationState(value) {
if (!this.animate) {
return;
}
this.state = value && this.eventService.expandMode !== PanelBarExpandMode.Single ? 'active' : 'activeWithoutAnimation';
}
/**
* @hidden
*/
toggleExpandedChildAnimations(value) {
if (this.childrenItems) {
this.childrenItems.forEach(child => {
if (child.expanded) {
child.toggleAnimationState(value);
child.toggleExpandedChildAnimations(value);
}
});
}
}
/**
* @hidden
*/
addLevelClass() {
if (this.level >= 0) {
this.renderer.addClass(this.element.nativeElement, `k-level-${this.level}`);
}
}
onWrapperFocusChange(focused) {
this.wrapperFocused = focused;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarItemComponent, deps: [{ token: PanelBarItemComponent, host: true, optional: true, skipSelf: true }, { token: PanelBarService }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PanelBarItemComponent, isStandalone: true, selector: "kendo-panelbar-item", inputs: { title: "title", id: "id", icon: "icon", iconClass: "iconClass", svgIcon: "svgIcon", imageUrl: "imageUrl", disabled: "disabled", expanded: "expanded", selected: "selected", content: "content", items: "items", template: "template" }, host: { properties: { "attr.role": "this.role", "attr.title": "this.titleAttribute", "class.k-panelbar-item": "this.kItemClass", "class.k-expanded": "this.kStateExpandedClass", "id": "this.itemId", "attr.aria-expanded": "this.ariaExpanded", "attr.aria-selected": "this.ariaSelected", "attr.aria-disabled": "this.ariaDisabled", "class.k-panelbar-header": "this.headerClass" } }, queries: [{ propertyName: "contentItems", predicate: PanelBarItemComponent }, { propertyName: "contentTemplate", predicate: PanelBarContentDirective }, { propertyName: "titleTemplates", predicate: PanelBarItemTitleDirective }], viewQueries: [{ propertyName: "header", first: true, predicate: ["header"], descendants: true }, { propertyName: "contentWrapper", first: true, predicate: ["contentWrapper"], descendants: true }, { propertyName: "viewChildItems", predicate: PanelBarItemComponent, descendants: true }], exportAs: ["kendoPanelbarItem"], ngImport: i0, template: `
<span
#header
[class.k-link]="true"
[class.k-selected]="!disabled && selected"
[class.k-focus]="focused && wrapperFocused"
[class.k-disabled]="disabled"
(click)="onItemClick($event)">
<kendo-icon-wrapper
*ngIf="icon || iconClass || svgIcon"
[name]="iconClasses"
[customFontClass]="customIconClasses"
[svgIcon]="svgIcon"
innerCssClass="k-panelbar-item-icon"
>
</kendo-icon-wrapper>
<img
*ngIf="imageUrl"
class="k-image k-panelbar-item-icon"
[src]="imageUrl"
alt="">
<ng-container *ngIf="!titleTemplate"><span class="k-panelbar-item-text">{{title}}</span></ng-container>
<ng-template *ngIf="titleTemplate"
[ngTemplateOutlet]="titleTemplate"
[ngTemplateOutletContext]="{
item: {
title: title,
id: id,
icon: icon,
iconClass: iconClass,
svgIcon: svgIcon,
imageUrl: imageUrl,
selected: selected,
expanded: expanded,
disabled: disabled,
focused: focused,
content: content
}
}"></ng-template>
<kendo-icon-wrapper
*ngIf="hasChildItems || hasContent"
[name]="expanded ? 'chevron-up' : 'chevron-down'"
[svgIcon]="expanderSVGIcon"
[innerCssClass]="dirInnerCssClasses"
>
</kendo-icon-wrapper>
</span>
<div
#contentWrapper
*ngIf="keepContent || (!disabled && expanded && (hasChildItems || hasContent))"
[@toggle]="state"
[attr.role]="'group'"
[attr.aria-hidden]="!disabled && !expanded"
>
<div
*ngIf="hasChildItems && !items?.length"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-group"
>
<ng-content select="kendo-panelbar-item"></ng-content>
</div>
<div
*ngIf="hasContent && !content"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-content">
<ng-template
[ngTemplateOutlet]="contentTemplate.first.templateRef"
[ngTemplateOutletContext]="{
$implicit: {
title: title,
id: id,
icon: icon,
imageUrl: imageUrl,
disabled: disabled,
content: content
}
}">
</ng-template>
</div>
<div *ngIf="hasItems"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-group">
<ng-container *ngFor="let item of items">
<kendo-panelbar-item *ngIf="!item.hidden"
[title]="item.title"
[id]="item.id"
[icon]="item.icon"
[iconClass]="item.iconClass"
[svgIcon]="item.svgIcon"
[imageUrl]="item.imageUrl"
[selected]="!!item.selected"
[expanded]="!!item.expanded"
[disabled]="!!item.disabled"
[template]="template"
[items]="item.children"
[content]="item.content">
</kendo-panelbar-item>
</ng-container>
</div>
<div
*ngIf="content"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-content">
<ng-template
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{
$implicit: {
title: title,
id: id,
icon: icon,
imageUrl: imageUrl,
disabled: disabled,
content: content
}
}">
</ng-template>
<ng-template [ngIf]="!template">{{content}}</ng-template>
</div>
</div>`, isInline: true, dependencies: [{ kind: "component", type: PanelBarItemComponent, selector: "kendo-panelbar-item", inputs: ["title", "id", "icon", "iconClass", "svgIcon", "imageUrl", "disabled", "expanded", "selected", "content", "items", "template"], exportAs: ["kendoPanelbarItem"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], animations: [
trigger('toggle', [
state('inactive', style({ display: 'none' })),
transition('* => active', [
style({ overflow: 'hidden', display: 'block', height: 0 }),
animate(200, style({ height: AUTO_STYLE }))
]),
transition('active => *', [
style({ overflow: 'hidden', height: AUTO_STYLE }),
animate(200, style({ height: 0, display: 'none' }))
])
])
] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarItemComponent, decorators: [{
type: Component,
args: [{
animations: [
trigger('toggle', [
state('inactive', style({ display: 'none' })),
transition('* => active', [
style({ overflow: 'hidden', display: 'block', height: 0 }),
animate(200, style({ height: AUTO_STYLE }))
]),
transition('active => *', [
style({ overflow: 'hidden', height: AUTO_STYLE }),
animate(200, style({ height: 0, display: 'none' }))
])
])
],
exportAs: 'kendoPanelbarItem',
selector: "kendo-panelbar-item",
template: `
<span
#header
[class.k-link]="true"
[class.k-selected]="!disabled && selected"
[class.k-focus]="focused && wrapperFocused"
[class.k-disabled]="disabled"
(click)="onItemClick($event)">
<kendo-icon-wrapper
*ngIf="icon || iconClass || svgIcon"
[name]="iconClasses"
[customFontClass]="customIconClasses"
[svgIcon]="svgIcon"
innerCssClass="k-panelbar-item-icon"
>
</kendo-icon-wrapper>
<img
*ngIf="imageUrl"
class="k-image k-panelbar-item-icon"
[src]="imageUrl"
alt="">
<ng-container *ngIf="!titleTemplate"><span class="k-panelbar-item-text">{{title}}</span></ng-container>
<ng-template *ngIf="titleTemplate"
[ngTemplateOutlet]="titleTemplate"
[ngTemplateOutletContext]="{
item: {
title: title,
id: id,
icon: icon,
iconClass: iconClass,
svgIcon: svgIcon,
imageUrl: imageUrl,
selected: selected,
expanded: expanded,
disabled: disabled,
focused: focused,
content: content
}
}"></ng-template>
<kendo-icon-wrapper
*ngIf="hasChildItems || hasContent"
[name]="expanded ? 'chevron-up' : 'chevron-down'"
[svgIcon]="expanderSVGIcon"
[innerCssClass]="dirInnerCssClasses"
>
</kendo-icon-wrapper>
</span>
<div
#contentWrapper
*ngIf="keepContent || (!disabled && expanded && (hasChildItems || hasContent))"
[@toggle]="state"
[attr.role]="'group'"
[attr.aria-hidden]="!disabled && !expanded"
>
<div
*ngIf="hasChildItems && !items?.length"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-group"
>
<ng-content select="kendo-panelbar-item"></ng-content>
</div>
<div
*ngIf="hasContent && !content"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-content">
<ng-template
[ngTemplateOutlet]="contentTemplate.first.templateRef"
[ngTemplateOutletContext]="{
$implicit: {
title: title,
id: id,
icon: icon,
imageUrl: imageUrl,
disabled: disabled,
content: content
}
}">
</ng-template>
</div>
<div *ngIf="hasItems"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-group">
<ng-container *ngFor="let item of items">
<kendo-panelbar-item *ngIf="!item.hidden"
[title]="item.title"
[id]="item.id"
[icon]="item.icon"
[iconClass]="item.iconClass"
[svgIcon]="item.svgIcon"
[imageUrl]="item.imageUrl"
[selected]="!!item.selected"
[expanded]="!!item.expanded"
[disabled]="!!item.disabled"
[template]="template"
[items]="item.children"
[content]="item.content">
</kendo-panelbar-item>
</ng-container>
</div>
<div
*ngIf="content"
[style.overflow]="contentOverflow"
[style.height]="contentHeight"
class="k-panelbar-content">
<ng-template
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{
$implicit: {
title: title,
id: id,
icon: icon,
imageUrl: imageUrl,
disabled: disabled,
content: content
}
}">
</ng-template>
<ng-template [ngIf]="!template">{{content}}</ng-template>
</div>
</div>`,
standalone: true,
imports: [NgIf, IconWrapperComponent, NgTemplateOutlet, NgFor]
}]
}], ctorParameters: function () { return [{ type: PanelBarItemComponent, decorators: [{
type: SkipSelf
}, {
type: Host
}, {
type: Optional
}] }, { type: PanelBarService }, { type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { title: [{
type: Input
}], id: [{
type: Input
}], icon: [{
type: Input
}], iconClass: [{
type: Input
}], svgIcon: [{
type: Input
}], imageUrl: [{
type: Input
}], disabled: [{
type: Input
}], expanded: [{
type: Input
}], selected: [{
type: Input
}], content: [{
type: Input
}], items: [{
type: Input
}], template: [{
type: Input
}], header: [{
type: ViewChild,
args: ['header', { static: false }]
}], contentWrapper: [{
type: ViewChild,
args: ['contentWrapper', { static: false }]
}], role: [{
type: HostBinding,
args: ['attr.role']
}], titleAttribute: [{
type: HostBinding,
args: ['attr.title']
}], kItemClass: [{
type: HostBinding,
args: ['class.k-panelbar-item']
}], kStateExpandedClass: [{
type: HostBinding,
args: ['class.k-expanded']
}], itemId: [{
type: HostBinding,
args: ['id']
}], ariaExpanded: [{
type: HostBinding,
args: ['attr.aria-expanded']
}], ariaSelected: [{
type: HostBinding,
args: ['attr.aria-selected']
}], ariaDisabled: [{
type: HostBinding,
args: ['attr.aria-disabled']
}], headerClass: [{
type: HostBinding,
args: ['class.k-panelbar-header']
}], viewChildItems: [{
type: ViewChildren,
args: [PanelBarItemComponent]
}], contentItems: [{
type: ContentChildren,
args: [PanelBarItemComponent]
}], contentTemplate: [{
type: ContentChildren,
args: [PanelBarContentDirective, { descendants: false }]
}], titleTemplates: [{
type: ContentChildren,
args: [PanelBarItemTitleDirective, { descendants: false }]
}] } });
/**
* Represents the template directive of the PanelBar which helps to customize the item content.
*/
class PanelBarItemTemplateDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarItemTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PanelBarItemTemplateDirective, isStandalone: true, selector: "[kendoPanelBarItemTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PanelBarItemTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPanelBarItemTemplate]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }]; } });
/**
* @hidden
*/
class PreventableEvent {
prevented = false;
/**
* 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;
}
/**
* Returns `true` if the event was prevented
* by any of its subscribers.
*
* @returns `true` if the default action was prevented.
* Otherwise, returns `false`.
*/
isDefaultPrevented() {
return this.prevented;
}
/**
* @hidden
*/
constructor(args) {
Object.assign(this, args);
}
}
/**
* Arguments for the `collapse` event of the PanelBar.
*/
class PanelBarCollapseEvent extends PreventableEvent {
/**
* The item that will be collapsed.
*/
item;
}
/**
* Arguments for the `expand` event of the PanelBar.
*/
class PanelBarExpandEvent extends PreventableEvent {
/**
* The item that will be expanded.
*/
item;
}
/**
* Arguments for the `select` event of the PanelBar.
*/
class PanelBarSelectEvent extends PreventableEvent {
/**
* The item that will be selected.
*/
item;
}
/**
* Arguments for the `stateChange` event of the PanelBar.
*/
class PanelBarStateChangeEvent {
/**
* A collection of all modified items.
*/
items;
}
/**
* Arguments for the `itemClick` event of the PanelBar.
*/
class PanelBarItemClickEvent {
/**
* The clicked item.
*/
item;
/**
* The DOM event that triggered the `itemClick` event.
*/
originalEvent;
}
/**
* Represents the [Kendo UI PanelBar component for Angular]({% slug overview_panelbar %}).
*/
// TODO: add styles as input prop
class PanelBarComponent {
localization;
/**
* Sets the expand mode of the PanelBar through the `PanelBarExpandMode` enum ([see example]({% slug expandmodes_panelbar %})).
*
* The available modes are:
* - `"single"`—Expands only one item at a time. Expanding an item collapses the item that was previously expanded.
* - `"multiple"`—The default mode of the PanelBar.
* Expands more than one item at a time. Items can also be toggled.
* - `"full"`—Expands only one item at a time.
* The expanded area occupies the entire height of the PanelBar. Requires you to set the `height` property.
*/
expandMode = PanelBarExpandMode.Default;
/**
* Allows the PanelBar to modify the selected state of the items.
*/
selectable = true;
/**
* Sets the animate state of the PanelBar ([see example]({% slug animations_panelbar %})).
*/
animate = true;
/**
* Sets the height of the component when the `"full"` expand mode is used.
* This option is ignored in the `"multiple"` and `"single"` expand modes.
*/
height = '400px';
/**
* When set to `true`, the PanelBar renders the content of all items and they are persisted in the DOM
* ([see example]({% slug templates_panelbar %}#toc-collections)).
* By default, this option is set to `false`.
*/
get keepItemContent() {
return this._keepItemContent;
}
set keepItemContent(keepItemContent) {
this._keepItemContent = keepItemContent;
this.eventService.onKeepContent(keepItemContent);
}
/**
* Sets the items of the PanelBar as an array of `PanelBarItemModel` instances
* ([see example]({% slug items_panelbar %})).
*/
set items(data) {
if (data) {
this._items = parsePanelBarItems(data);
}
}
get items() {
return this._items;
}
/**
* Fires each time the user interacts with a PanelBar item
* ([see example](slug:routing_panelbar#using-router-service)).
* The event data contains a collection of all items that are modified.
*/
stateChange = new EventEmitter();
/**
* Fires when an item is about to be selected.
* ([see example]({% slug events_panelbar %}))
* This event is preventable. If you cancel it, the item will not be selected.
*/
select = new EventEmitter();
/**
* Fires when an item is about to be expanded.
* ([see example]({% slug events_panelbar %}))
* This event is preventable. If you cancel it, the item will remain collapsed.
*/
expand = new EventEmitter();
/**
* Fires when an item is about to be collapsed.
* ([see example]({% slug events_panelbar %}))
* This event is preventable. If you cancel it, the item will remain expanded.
*/
collapse = new EventEmitter();
/**
* Fires when the user clicks an item ([see example]({% slug events_panelbar %})).
*/
itemClick = new EventEmitter();
hostClasses = true;
tabIndex = 0;
role = 'tree';
activeDescendant = '';
get hostHeight() {
return this.expandMode === PanelBarExpandMode.Full ? this.height : 'auto';
}
get overflow() {
return this.expandMode === PanelBarExpandMode.Full ? 'hidden' : 'visible';
}
get dir() {
return this.localization.rtl ? 'rtl' : 'ltr';
}
template;
contentItems;
contentChildItems;
viewChildItems;
/**
* @hidden
*/
showLicenseWatermark = false;
allItems;
childrenItems;
isViewInit = true;
focused = false;
_items;
_keepItemContent = false;
elementRef;
eventService;
keyBindings;
subs = new Subscription();
constructor(elementRef, eventService, localization) {
this.localization = localization;
const isValid = validatePackage(packageMetadata);
this.showLicenseWatermark = shouldShowValidationUI(isValid);
/* eslint-disable-line*/
this.keyBindings = this.computedKeys;
this.elementRef = elementRef;
this.eventService = eventService;
this.subs.add(this.eventService.children$.subscribe(event => this.onItemAction(event)));
this.subs.add(this.eventService.itemClick.subscribe(ev => this.itemClick.emit(ev)));
}
/**
* @hidden
*/
invertKeys(original, inverted) {
return this.localization.rtl ? inverted : original;
}
get computedKeys() {
return {
[Keys.Space]: () => this.selectFocusedItem(),
[Keys.Enter]: () => this.selectFocusedItem(),
[Keys.ArrowUp]: () => this.focusPreviousItem(),
[this.invertKeys(Keys.ArrowLeft, Keys.ArrowRight)]: () => this.collapseItem(),
[Keys.ArrowDown]: () => this.focusNextItem(),
[this.invertKeys(Keys.ArrowRight, Keys.ArrowLeft)]: () => this.expandItem(),
[Keys.End]: () => this.focusLastItem(),
[Keys.Home]: () => this.focusFirstItem()
};
}
ngOnDestroy() {
this.subs.unsubscribe();
}
ngOnInit() {
this.subs.add(this.localization.changes.subscribe(() => this.keyBindings = this.computedKeys));
this.eventService.animate = this.animate;
this.eventService.expandMode = this.expandMode;
}
ngAfterViewChecked() {
if (this.items) {
this.childrenItems = this.viewChildItems.toArray();
this.allItems = this.viewItems;
}
else {
this.childrenItems = this.contentChildItems.toArray();
this.allItems = this.contentItems.toArray();
}
if (this.isViewInit && this.childrenItems.length) {
this.isViewInit = false;
setTimeout(() => this.updateChildrenHeight());
}
this.validateConfiguration();
}
ngOnChanges(changes) {
if (changes['height'] || changes['expandMode'] || changes['items']) { // eslint-disable-line
if (this.childrenItems) {
setTimeout(this.updateChildrenHeight);
}
}
if (changes['animate']) {
this.eventService.animate = this.animate;
}
if (changes['expandMode']) {
this.eventService.expandMode = this.expandMode;
}
}
get templateRef() {
return this.template ? this.template.templateRef : undefined;
}
/**
* @hidden
*/
onComponentClick(event) {
const itemClicked = this.visibleItems().some((item) => {
return item.header.nativeElement.contains(event.target);
});
if (!isFocusable(event.target) && !this.focused && itemClicked) {
this.elementRef.nativeElement.focus();
}
}
/**
* @hidden
*/
onComponentFocus() {
this.eventService.onFocus();
this.focused = true;
if (this.allItems.length > 0) {
const visibleItems = this.visibleItems();
const focusedItems = visibleItems.filter(item => item.focused);
if (!focusedItems.length && visibleItems.length > 0) {
visibleItems[0].focused = true;
this.activeDescendant = visibleItems[0].itemId;
}
}
}
/**
* @hidden
*/
onComponentBlur() {
this.eventService.onBlur();
this.focused = false;
this.activeDescendant = '';
}
/**
* @hidden
*/
onComponentKeyDown(event) {
if (event.target === this.elementRef.nativeElement) {
if (event.keyCode === Keys.Space || event.keyCode === Keys.ArrowUp || event.keyCode === Keys.ArrowDown ||
event.keyCode === Keys.ArrowLeft || event.keyCode === Keys.ArrowRight || event.keyCode === Keys.Home ||
event.keyCode === Keys.End || event.keyCode === Keys.PageUp || event.keyCode === Keys.PageDown) {
event.preventDefault();
}
const handler = this.keyBindings[event.keyCode];
//TODO: check if next item is disabled and skip operation?
if (handler) {
handler();
}
}
}
/**
* @hidden
*/
emitEvent(event, item) {
let eventArgs;
switch (event) {
case 'select':
eventArgs = new PanelBarSelectEvent();
break;
case 'collapse':
eventArgs = new PanelBarCollapseEvent();
break;
default:
eventArgs = new PanelBarExpandEvent();
break;
}
eventArgs.item = item.serialize();
this[event].emit(eventArgs);
return eventArgs;
}
get viewItems() {
let treeItems = [];
this.viewChildItems.toArray().forEach(item => {
treeItems.push(item);
treeItems = treeItems.concat(item.subTreeViewItems());
});
return treeItems;
}
validateConfiguration() {
if (isDevMode()) {
if (this.items && (this.contentItems && this.contentItems.length > 0)) {
throw new Error('Invalid configuration: mixed template components and items property.');
}
}
}
updateChildrenHeight = () => {
let child