@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
668 lines (667 loc) • 27.7 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Component, Input, ContentChildren, ViewChildren, Optional, Host, HostBinding, SkipSelf, isDevMode, TemplateRef, QueryList, ViewChild, ElementRef, Renderer2 } from '@angular/core';
import { animate, trigger, style, state, transition, AUTO_STYLE } from '@angular/animations';
import { PanelBarService } from "./panelbar.service";
import { PanelBarContentDirective } from "./panelbar-content.directive";
import { PanelBarItemTitleDirective } from "./panelbar-item-title.directive";
import { isFocusable } from '../common/dom-queries';
import { Subscription } from 'rxjs';
import { PanelBarExpandMode } from './panelbar-expand-mode';
import { chevronDownIcon, chevronUpIcon } from '@progress/kendo-svg-icons';
import { IconWrapperComponent } from '@progress/kendo-angular-icons';
import { NgIf, NgTemplateOutlet, NgFor } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "./panelbar.service";
/**
* @hidden
*/
let nextId = 0;
/**
* Represents the items of the PanelBar.
*/
export 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: i1.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: i1.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 }]
}] } });