UNPKG

@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
/**----------------------------------------------------------------------------------------- * 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 }] }] } });