UNPKG

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