@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
308 lines (307 loc) • 13.8 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, ContentChildren, ElementRef, HostBinding, Input, NgZone, QueryList, Renderer2 } from '@angular/core';
import { LocalizationService } from '@progress/kendo-angular-l10n';
import { Subscription } from 'rxjs';
import { TileLayoutDraggingService } from './dragging-service';
import { TileLayoutKeyboardNavigationService } from './keyboard-navigation.service';
import { isPresent } from './../common/util';
import { RESIZE_DIRECTIONS, RTL_RESIZE_DIRECTIONS } from './constants';
import { TileLayoutItemHeaderComponent } from './tilelayout-item-header.component';
import { getId } from './util';
import { TileLayoutResizeHandleDirective } from './tilelayout-resize-handle.directive';
import { NgIf, NgFor } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-l10n";
import * as i2 from "./dragging-service";
import * as i3 from "./keyboard-navigation.service";
/**
* Represents a tile item within the TileLayoutComponent.
*/
export class TileLayoutItemComponent {
elem;
zone;
renderer;
localization;
draggingService;
keyboardNavigationService;
/**
* The title that will be rendered in the item header ([see example]({% slug tiles_tilelayout %}#toc-tiles-configuration)).
*/
title;
/**
* Determines how many rows will the tile item span ([see example](slug:resizing_tilelayout#programmatic-resizing)).
* @default 1
*/
rowSpan = 1;
/**
* Determines how many columns will the tile item span ([see example](slug:resizing_tilelayout#programmatic-resizing)).
* @default 1
*/
colSpan = 1;
/**
* Determines the order of the tile items within the TileLayout.
* If not set, the items will receive increasing sequential order in accordance with
* their position in the DOM when initially rendered.
*/
set order(value) {
this._order = value;
this.renderer.setStyle(this.elem.nativeElement, 'order', `${this._order}`);
}
get order() {
return this._order;
}
/**
* Sets the starting column of the item ([see example](slug:tiles_tilelayout#size-and-position)).
*/
col;
/**
* Sets the starting row of the item ([see example](slug:tiles_tilelayout#size-and-position)).
*/
row;
/**
* Determines whether the item can be reordered. By default all items are reorderable when the [reorderable]({% slug api_layout_tilelayoutcomponent %}#toc-reorderable) property of the TileLayoutComponent is set to `true` ([see example]({% slug reordering_tilelayout %}#toc-disabling-reordering)).
*
* @default true
*/
reorderable = true;
/**
* Determines whether the item can be resized. By default all items are resizable when the [resizable]({% slug api_layout_tilelayoutcomponent %}#resizable) property of the TileLayoutComponent is set to `true` ([see example](slug:resizing_tilelayout#toc-disabling-resizing)).
* @default true
*/
resizable = true;
itemClass = true;
hostRole = 'listitem';
get hostDropEffect() {
return this.isResizable || this.isReorderable ? 'execute' : undefined;
}
get hostTabindex() {
return this.isNavigable ? '0' : undefined;
}
get ariaKeyShortcuts() {
return this.isNavigable ? 'Enter' : undefined;
}
get hostGrabbed() {
return this.isResizable || this.isReorderable;
}
get hostLabelledBy() {
return this.title ? this.titleId : undefined;
}
get colEnd() {
return `span ${this.colSpan}`;
}
get rowEnd() {
return `span ${this.rowSpan}`;
}
get colStart() {
return isPresent(this.col) ? this.col.toString() : undefined;
}
get rowStart() {
return isPresent(this.row) ? this.row.toString() : undefined;
}
/**
* @hidden
*/
get isReorderable() {
return this.reorderable && this.draggingService.reorderable.getValue();
}
/**
* @hidden
*/
get isNavigable() {
return this.keyboardNavigationService.navigable.getValue();
}
/**
* @hidden
*/
get isResizable() {
return this.resizable && this.draggingService.resizable.getValue();
}
/**
* @hidden
*/
resizeDirections;
/**
* @hidden
*/
rtl;
headers;
/**
* @hidden
*/
titleId = '';
subs = new Subscription();
keyboardNavigationSubs;
focusableItems;
_order;
constructor(elem, zone, renderer, localization, draggingService, keyboardNavigationService) {
this.elem = elem;
this.zone = zone;
this.renderer = renderer;
this.localization = localization;
this.draggingService = draggingService;
this.keyboardNavigationService = keyboardNavigationService;
this.subs.add(this.localization.changes.subscribe(({ rtl }) => {
this.rtl = rtl;
}));
this.subs.add(this.draggingService.resizable.subscribe(resizable => {
this.resizeDirections = resizable && this.resizable ?
this.rtl ? RTL_RESIZE_DIRECTIONS : RESIZE_DIRECTIONS : undefined;
}));
this.titleId = getId('k-tilelayout-title');
}
ngAfterViewInit() {
const elem = this.elem.nativeElement;
const keyboardNavigation = this.keyboardNavigationService;
this.subs.add(this.draggingService.reorderable.subscribe(reorderable => {
this.toggleCursorClass(reorderable && this.reorderable);
}));
this.subs.add(keyboardNavigation.navigable.subscribe(isNavigable => {
if (isNavigable) {
this.keyboardNavigationSubs = new Subscription();
this.focusableItems = keyboardNavigation.getAllFocusableChildren(elem);
this.zone.runOutsideAngular(() => {
keyboardNavigation.changeTabIndex('-1', elem, this.focusableItems);
this.keyboardNavigationSubs.add(this.renderer.listen(elem, 'keydown', event => keyboardNavigation.onKeyDown(event, elem, this.focusableItems, this.draggingService.tileLayoutSettings)));
this.keyboardNavigationSubs.add(this.renderer.listen(elem, 'mousedown', event => keyboardNavigation.onMousedown(event, elem, this.focusableItems, this)));
this.keyboardNavigationSubs.add(this.renderer.listen(elem, 'focusout', event => keyboardNavigation.onFocusOut(event, elem, this.focusableItems)));
});
}
else if (this.focusableItems) {
this.focusableItems.forEach((focusItem) => {
this.renderer.setAttribute(focusItem, 'tabindex', '0');
});
this.keyboardNavigationSubs.unsubscribe();
}
}));
}
ngOnChanges(changes) {
if (changes['reorderable'] && !changes['reorderable'].firstChange) {
this.toggleCursorClass(changes['reorderable'].currentValue && this.draggingService.reorderable.getValue());
}
if (changes['resizable']) {
this.resizeDirections = this.resizable && this.draggingService.resizable.getValue() ?
this.rtl ? RTL_RESIZE_DIRECTIONS : RESIZE_DIRECTIONS : undefined;
}
}
ngOnDestroy() {
this.subs.unsubscribe();
if (this.keyboardNavigationSubs) {
this.keyboardNavigationSubs.unsubscribe();
}
}
/**
* @hidden
*/
focus() {
this.elem.nativeElement.focus();
}
toggleCursorClass(isReorderable) {
const headerEl = this.elem.nativeElement.querySelector('.k-tilelayout-item-header');
if (!headerEl) {
return;
}
if (isReorderable) {
this.renderer.addClass(headerEl, 'k-cursor-move');
}
else {
this.renderer.removeClass(headerEl, 'k-cursor-move');
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TileLayoutItemComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i1.LocalizationService }, { token: i2.TileLayoutDraggingService }, { token: i3.TileLayoutKeyboardNavigationService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: TileLayoutItemComponent, isStandalone: true, selector: "kendo-tilelayout-item", inputs: { title: "title", rowSpan: "rowSpan", colSpan: "colSpan", order: "order", col: "col", row: "row", reorderable: "reorderable", resizable: "resizable" }, host: { properties: { "class.k-tilelayout-item": "this.itemClass", "class.k-card": "this.itemClass", "attr.role": "this.hostRole", "attr.aria-dropeffect": "this.hostDropEffect", "attr.tabindex": "this.hostTabindex", "attr.aria-keyshortcuts": "this.ariaKeyShortcuts", "attr.aria-grabbed": "this.hostGrabbed", "attr.aria-labelledby": "this.hostLabelledBy", "style.grid-column-end": "this.colEnd", "style.grid-row-end": "this.rowEnd", "style.grid-column-start": "this.colStart", "style.grid-row-start": "this.rowStart" } }, queries: [{ propertyName: "headers", predicate: TileLayoutItemHeaderComponent }], usesOnChanges: true, ngImport: i0, template: `
<kendo-tilelayout-item-header *ngIf="title">
<h5 [id]="titleId" class="k-card-title">{{ title }}</h5>
</kendo-tilelayout-item-header>
<ng-content></ng-content>
<ng-container *ngIf="resizable">
<div
*ngFor="let dir of resizeDirections"
class="k-resize-handle k-cursor-{{dir}}-resize"
kendoTileLayoutResizeHandle
[rtl]="rtl"
[resizeDirection]="dir">
</div>
</ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TileLayoutItemHeaderComponent, selector: "kendo-tilelayout-item-header" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: TileLayoutResizeHandleDirective, selector: "[kendoTileLayoutResizeHandle]", inputs: ["resizeDirection", "rtl"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TileLayoutItemComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-tilelayout-item',
template: `
<kendo-tilelayout-item-header *ngIf="title">
<h5 [id]="titleId" class="k-card-title">{{ title }}</h5>
</kendo-tilelayout-item-header>
<ng-content></ng-content>
<ng-container *ngIf="resizable">
<div
*ngFor="let dir of resizeDirections"
class="k-resize-handle k-cursor-{{dir}}-resize"
kendoTileLayoutResizeHandle
[rtl]="rtl"
[resizeDirection]="dir">
</div>
</ng-container>
`,
standalone: true,
imports: [NgIf, TileLayoutItemHeaderComponent, NgFor, TileLayoutResizeHandleDirective]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i1.LocalizationService }, { type: i2.TileLayoutDraggingService }, { type: i3.TileLayoutKeyboardNavigationService }]; }, propDecorators: { title: [{
type: Input
}], rowSpan: [{
type: Input
}], colSpan: [{
type: Input
}], order: [{
type: Input
}], col: [{
type: Input
}], row: [{
type: Input
}], reorderable: [{
type: Input
}], resizable: [{
type: Input
}], itemClass: [{
type: HostBinding,
args: ['class.k-tilelayout-item']
}, {
type: HostBinding,
args: ['class.k-card']
}], hostRole: [{
type: HostBinding,
args: ['attr.role']
}], hostDropEffect: [{
type: HostBinding,
args: ['attr.aria-dropeffect']
}], hostTabindex: [{
type: HostBinding,
args: ['attr.tabindex']
}], ariaKeyShortcuts: [{
type: HostBinding,
args: ['attr.aria-keyshortcuts']
}], hostGrabbed: [{
type: HostBinding,
args: ['attr.aria-grabbed']
}], hostLabelledBy: [{
type: HostBinding,
args: ['attr.aria-labelledby']
}], colEnd: [{
type: HostBinding,
args: ['style.grid-column-end']
}], rowEnd: [{
type: HostBinding,
args: ['style.grid-row-end']
}], colStart: [{
type: HostBinding,
args: ['style.grid-column-start']
}], rowStart: [{
type: HostBinding,
args: ['style.grid-row-start']
}], headers: [{
type: ContentChildren,
args: [TileLayoutItemHeaderComponent]
}] } });