@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
418 lines (407 loc) • 20.6 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 { Component, ElementRef, HostBinding, HostListener, Input, Renderer2, ViewChild } from '@angular/core';
import { chevronRightIcon } from '@progress/kendo-svg-icons';
import { Keys, isChanged } from '@progress/kendo-angular-common';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { TimelineService } from './timeline.service';
import { TimelineCardHeaderTemplateDirective } from './templates/timeline-card-header.directive';
import { TimelineCardBodyTemplateDirective } from './templates/timeline-card-body.directive';
import { TimelineCardActionsTemplateDirective } from './templates/timeline-card-actions.directive';
import { CardActionsComponent } from '../card/card-actions.component';
import { CardMediaDirective } from '../card/directives/card-media.directive';
import { CardBodyComponent } from '../card/card-body.component';
import { CardSubtitleDirective } from '../card/directives/card-subtitle.directive';
import { ButtonComponent } from '@progress/kendo-angular-buttons';
import { CardTitleDirective } from '../card/directives/card-title.directive';
import { CardHeaderComponent } from '../card/card-header.component';
import { NgStyle, NgClass, NgIf, NgTemplateOutlet, NgFor } from '@angular/common';
import { CardComponent } from '../card/card.component';
import * as i0 from "@angular/core";
import * as i1 from "./timeline.service";
/**
* @hidden
*/
export class TimelineCardComponent {
element;
timelineService;
renderer;
event;
expanded = false;
collapsible = true;
reversed = false;
orientation;
navigable;
tabIndex;
animationDuration;
index;
eventWidth;
eventHeight;
headerTemplate;
bodyTemplate;
actionsTemplate;
set calloutStyle(value) {
// applies only to horizontal orientation where the callout points upwards
if (!this.calloutElementRef || !this.calloutElementRef.nativeElement.classList.contains('k-callout-n')) {
return;
}
this.calloutElementRef.nativeElement.style.left = value?.left;
}
calloutElementRef;
hostClass = true;
get collapsedClass() {
return !this.expanded && !this.animationInProgress && this.animationState === 'collapsed';
}
onComponentKeyDown(event) {
if (!this.navigable) {
return;
}
if (event.keyCode === Keys.Space || event.keyCode === Keys.Enter) {
event.preventDefault();
if (this.collapsible && this.orientation === 'vertical') {
this.expanded = !this.expanded;
}
}
}
get role() {
return this.orientation === 'vertical' ? 'button' : 'tabpanel';
}
get ariaLive() {
return this.orientation === 'vertical' ? 'polite' : null;
}
get ariaExpanded() {
return this.orientation === 'vertical' ? this.expanded : null;
}
calloutSvgIcon = chevronRightIcon;
calloutFontIcon = 'arrow-chevron-right';
animationState = this.expanded ? 'expanded' : 'collapsed';
animationInProgress = false;
constructor(element, timelineService, renderer) {
this.element = element;
this.timelineService = timelineService;
this.renderer = renderer;
}
ngAfterViewInit() {
this.makeOverflowVisible();
}
ngOnChanges(changes) {
if (isChanged('collapsible', changes, false)) {
if (!this.collapsible) {
this.expanded = true;
}
}
}
toggle() {
if (this.orientation === 'vertical') {
this.expanded = this.collapsible ? !this.expanded : true;
this.timelineService.onToggle(this.index);
}
}
expand() {
if (!this.expanded) {
this.expanded = true;
this.timelineService.onToggle(this.index);
}
}
collapse() {
if (!this.collapsible || !this.expanded) {
return;
}
this.expanded = false;
this.timelineService.onToggle(this.index);
}
onActionClick(event) {
event.stopPropagation();
this.timelineService.onActionClick(this.index);
}
animationStart() {
this.animationInProgress = true;
}
animationDone(event) {
this.animationInProgress = false;
if (event.toState === 'expanded' && event.fromState !== 'expanded') {
this.animationState = 'expanded';
this.makeOverflowVisible();
}
else if (event.toState === 'collapsed' && event.fromState !== 'collapsed') {
this.animationState = 'collapsed';
}
}
makeOverflowVisible() {
if (this.orientation === 'vertical') {
return;
}
const cardBody = this.element?.nativeElement.querySelector('.k-card-body');
if (cardBody) {
this.renderer.setStyle(cardBody, 'overflow', 'auto');
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TimelineCardComponent, deps: [{ token: i0.ElementRef }, { token: i1.TimelineService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: TimelineCardComponent, isStandalone: true, selector: "kendo-timeline-card", inputs: { event: "event", expanded: "expanded", collapsible: "collapsible", reversed: "reversed", orientation: "orientation", navigable: "navigable", tabIndex: "tabIndex", animationDuration: "animationDuration", index: "index", eventWidth: "eventWidth", eventHeight: "eventHeight", headerTemplate: "headerTemplate", bodyTemplate: "bodyTemplate", actionsTemplate: "actionsTemplate", calloutStyle: "calloutStyle" }, host: { listeners: { "keydown": "onComponentKeyDown($event)" }, properties: { "class.k-timeline-card": "this.hostClass", "class.k-collapsed": "this.collapsedClass" } }, providers: [], viewQueries: [{ propertyName: "calloutElementRef", first: true, predicate: ["callout"], descendants: true }], exportAs: ["kendoTimelineCard"], usesOnChanges: true, ngImport: i0, template: `
<kendo-card
[ngStyle]="{ 'height': orientation === 'horizontal' ? eventHeight + 'px' : null }"
[width]="orientation === 'horizontal' ? 'auto' : eventWidth + 'px'"
class="k-card-with-callout k-card-vertical"
(click)="toggle()"
[attr.role]="role"
[attr.aria-live]="ariaLive"
[attr.aria-expanded]="ariaExpanded"
[attr.tabindex]="tabIndex"
>
<span
#callout
class="k-timeline-card-callout k-card-callout"
[ngClass]="{
'k-callout-n': orientation === 'horizontal',
'k-callout-w': orientation === 'vertical' && !this.reversed,
'k-callout-e': orientation === 'vertical' && this.reversed
}"
>
</span>
<div *ngIf="event" class="k-card-inner">
<kendo-card-header>
<ng-template
*ngIf="headerTemplate"
[ngTemplateOutlet]="headerTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: event, index: index}">
>
</ng-template>
<div kendoCardTitle *ngIf="!headerTemplate">
<span class="k-event-title">{{ event.title }}</span>
<button
kendoButton
*ngIf="collapsible && orientation === 'vertical'"
[icon]="calloutFontIcon"
[svgIcon]="calloutSvgIcon"
class="k-event-collapse"
fillMode="flat"
[attr.aria-hidden]="true"
tabindex="-1"
type="button"
></button>
</div>
<div kendoCardSubtitle *ngIf="!headerTemplate">{{ event.subtitle }}</div>
</kendo-card-header>
<kendo-card-body
*ngIf="event.description || event.images"
[@toggle]="{value: expanded ? 'expanded' : 'collapsed', params: {animationDuration: this.animationDuration || 0}}"
(@toggle.start)="animationStart()"
(@toggle.done)="animationDone($event)"
>
<ng-template
*ngIf="bodyTemplate"
[ngTemplateOutlet]="bodyTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: event, index: index}">
>
</ng-template>
<div *ngIf="!bodyTemplate" class="k-card-description">
<p *ngIf="event.description">{{ event.description }}</p>
<ng-container *ngFor="let image of event.images">
<img *ngIf="image.alt" kendoCardMedia [src]="image.src" [alt]="image.alt" />
<img *ngIf="!image.alt" kendoCardMedia [src]="image.src" />
</ng-container>
</div>
</kendo-card-body>
<kendo-card-actions
*ngIf="event.actions"
[@toggle]="{value: expanded ? 'expanded' : 'collapsed', params: {animationDuration: this.animationDuration || 0}}"
>
<ng-template
*ngIf="actionsTemplate"
[ngTemplateOutlet]="actionsTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: event, index: index}">
>
</ng-template>
<ng-container *ngIf="!actionsTemplate">
<a *ngFor="let action of event.actions"
[href]="action.url"
[target]="action.target === 'blank' ? '_blank' : '_self'"
(click)="onActionClick($event)"
class="k-button k-button-md k-rounded-md k-button-flat k-button-flat-primary"
role="button"
>
{{ action.text }}
</a>
</ng-container>
</kendo-card-actions>
</div>
</kendo-card>
`, isInline: true, dependencies: [{ kind: "component", type: CardComponent, selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CardHeaderComponent, selector: "kendo-card-header" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: CardTitleDirective, selector: "[kendoCardTitle]" }, { 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"] }, { kind: "directive", type: CardSubtitleDirective, selector: "[kendoCardSubtitle]" }, { kind: "component", type: CardBodyComponent, selector: "kendo-card-body" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: CardMediaDirective, selector: "[kendoCardMedia]" }, { kind: "component", type: CardActionsComponent, selector: "kendo-card-actions", inputs: ["orientation", "layout", "actions"], outputs: ["action"] }], animations: [
trigger('toggle', [
state('collapsed', style({
height: '0',
'overflow-y': 'hidden',
display: 'none'
})),
state('expanded', style({
height: '*',
'overflow-y': 'hidden',
display: 'block'
})),
transition('collapsed <=> expanded', [
animate('{{animationDuration}}ms')
], { params: { animationDuration: '400' } }),
])
] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TimelineCardComponent, decorators: [{
type: Component,
args: [{
animations: [
trigger('toggle', [
state('collapsed', style({
height: '0',
'overflow-y': 'hidden',
display: 'none'
})),
state('expanded', style({
height: '*',
'overflow-y': 'hidden',
display: 'block'
})),
transition('collapsed <=> expanded', [
animate('{{animationDuration}}ms')
], { params: { animationDuration: '400' } }),
])
],
providers: [],
exportAs: 'kendoTimelineCard',
selector: 'kendo-timeline-card',
template: `
<kendo-card
[ngStyle]="{ 'height': orientation === 'horizontal' ? eventHeight + 'px' : null }"
[width]="orientation === 'horizontal' ? 'auto' : eventWidth + 'px'"
class="k-card-with-callout k-card-vertical"
(click)="toggle()"
[attr.role]="role"
[attr.aria-live]="ariaLive"
[attr.aria-expanded]="ariaExpanded"
[attr.tabindex]="tabIndex"
>
<span
#callout
class="k-timeline-card-callout k-card-callout"
[ngClass]="{
'k-callout-n': orientation === 'horizontal',
'k-callout-w': orientation === 'vertical' && !this.reversed,
'k-callout-e': orientation === 'vertical' && this.reversed
}"
>
</span>
<div *ngIf="event" class="k-card-inner">
<kendo-card-header>
<ng-template
*ngIf="headerTemplate"
[ngTemplateOutlet]="headerTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: event, index: index}">
>
</ng-template>
<div kendoCardTitle *ngIf="!headerTemplate">
<span class="k-event-title">{{ event.title }}</span>
<button
kendoButton
*ngIf="collapsible && orientation === 'vertical'"
[icon]="calloutFontIcon"
[svgIcon]="calloutSvgIcon"
class="k-event-collapse"
fillMode="flat"
[attr.aria-hidden]="true"
tabindex="-1"
type="button"
></button>
</div>
<div kendoCardSubtitle *ngIf="!headerTemplate">{{ event.subtitle }}</div>
</kendo-card-header>
<kendo-card-body
*ngIf="event.description || event.images"
[@toggle]="{value: expanded ? 'expanded' : 'collapsed', params: {animationDuration: this.animationDuration || 0}}"
(@toggle.start)="animationStart()"
(@toggle.done)="animationDone($event)"
>
<ng-template
*ngIf="bodyTemplate"
[ngTemplateOutlet]="bodyTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: event, index: index}">
>
</ng-template>
<div *ngIf="!bodyTemplate" class="k-card-description">
<p *ngIf="event.description">{{ event.description }}</p>
<ng-container *ngFor="let image of event.images">
<img *ngIf="image.alt" kendoCardMedia [src]="image.src" [alt]="image.alt" />
<img *ngIf="!image.alt" kendoCardMedia [src]="image.src" />
</ng-container>
</div>
</kendo-card-body>
<kendo-card-actions
*ngIf="event.actions"
[@toggle]="{value: expanded ? 'expanded' : 'collapsed', params: {animationDuration: this.animationDuration || 0}}"
>
<ng-template
*ngIf="actionsTemplate"
[ngTemplateOutlet]="actionsTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: event, index: index}">
>
</ng-template>
<ng-container *ngIf="!actionsTemplate">
<a *ngFor="let action of event.actions"
[href]="action.url"
[target]="action.target === 'blank' ? '_blank' : '_self'"
(click)="onActionClick($event)"
class="k-button k-button-md k-rounded-md k-button-flat k-button-flat-primary"
role="button"
>
{{ action.text }}
</a>
</ng-container>
</kendo-card-actions>
</div>
</kendo-card>
`,
standalone: true,
imports: [CardComponent, NgStyle, NgClass, NgIf, CardHeaderComponent, NgTemplateOutlet, CardTitleDirective, ButtonComponent, CardSubtitleDirective, CardBodyComponent, NgFor, CardMediaDirective, CardActionsComponent]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.TimelineService }, { type: i0.Renderer2 }]; }, propDecorators: { event: [{
type: Input
}], expanded: [{
type: Input
}], collapsible: [{
type: Input
}], reversed: [{
type: Input
}], orientation: [{
type: Input
}], navigable: [{
type: Input
}], tabIndex: [{
type: Input
}], animationDuration: [{
type: Input
}], index: [{
type: Input
}], eventWidth: [{
type: Input
}], eventHeight: [{
type: Input
}], headerTemplate: [{
type: Input
}], bodyTemplate: [{
type: Input
}], actionsTemplate: [{
type: Input
}], calloutStyle: [{
type: Input
}], calloutElementRef: [{
type: ViewChild,
args: ['callout']
}], hostClass: [{
type: HostBinding,
args: ['class.k-timeline-card']
}], collapsedClass: [{
type: HostBinding,
args: ['class.k-collapsed']
}], onComponentKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });