UNPKG

@progress/kendo-angular-layout

Version:

Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts

320 lines (319 loc) 13.7 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, ElementRef, EventEmitter, ContentChildren, Host, HostBinding, Inject, Input, Optional, Output, QueryList, ViewChildren, Renderer2, NgZone, isDevMode } from '@angular/core'; import { LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n'; import { validatePackage } from '@progress/kendo-licensing'; import { isDocumentAvailable } from '@progress/kendo-angular-common'; import { packageMetadata } from '../package-metadata'; import { SplitterPaneComponent } from './splitter-pane.component'; import { SplitterBarComponent } from './splitter-bar.component'; import { SplitterService } from './splitter.service'; import { isPresent } from '../common/util'; import { DraggableDirective } from '@progress/kendo-angular-common'; import { NgClass, NgFor, NgIf, NgStyle } from '@angular/common'; import * as i0 from "@angular/core"; import * as i1 from "./splitter.service"; import * as i2 from "@progress/kendo-angular-l10n"; import * as i3 from "./splitter-pane.component"; const SIZING_DOC_LINK = 'https://www.telerik.com/kendo-angular-ui/components/layout/splitter/panes/#toc-size'; /** * Represents the [Kendo UI Splitter component for Angular]({% slug overview_splitter %}). * * ```ts-preview * * @Component({ * selector: 'my-app', * template: ` * <kendo-splitter [style.height.px]="280"> * * <kendo-splitter-pane [collapsible]="true" size="30%"> * <h3>Inner splitter / left pane</h3> * <p>Resizable and collapsible.</p> * </kendo-splitter-pane> * * <kendo-splitter-pane> * <h3>Inner splitter / center pane</h3> * <p>Resizable only.</p> * </kendo-splitter-pane> * * <kendo-splitter-pane [collapsible]="true" size="30%"> * <h3>Inner splitter / right pane</h3> * <p>Resizable and collapsible.</p> * </kendo-splitter-pane> * * </kendo-splitter> * `, * styles: [ ` * h3 { font-size: 1.2em; } * h3, p { margin: 10px; padding: 0; } * ` ] * }) * class AppComponent {} * ``` */ export class SplitterComponent { element; splitterService; localization; renderer; ngZone; enclosingPane; /** * Specifies the orientation of the panes within the Splitter. * Panes in a horizontal Splitter are placed horizontally. * Panes in a vertical Splitter are placed vertically. */ orientation = 'horizontal'; /** * Sets the width or height (depending on the orientation) of the Splitter splitbars in pixels. */ splitbarWidth; /** * The distance in pixels that the separator is moved with during keyboard navigation. * @default 10 */ set resizeStep(value) { this.splitterService.resizeStep = value; } get resizeStep() { return this.splitterService.resizeStep; } /** * The CSS classes that will be rendered on the splitter bars. * Supports the type of values that are supported by [`ngClass`](link:site.data.urls.angular['ngclassapi']). */ splitterBarClass; /** * Fires after a Splitter pane is resized or collapsed. * Useful for triggering layout calculations on components * which are positioned inside the panes. */ layoutChange; get hostClasses() { return true; } get horizontalHostClasses() { return this.orientation === 'horizontal'; } get verticalHostClasses() { return this.orientation === 'vertical'; } get dir() { return this.direction; } set splitbars(splitbars) { this.splitterService.splitterBars = splitbars ? splitbars.toArray() : []; if (!isPresent(splitbars) || !isPresent(this.panes)) { return; } if (!isDocumentAvailable()) { return; } const panesArray = this.panes.toArray(); const splitBarsArray = splitbars.toArray(); const components = [...panesArray, ...splitBarsArray] .sort((a, b) => a.order - b.order); const elements = components.map(component => component.element.nativeElement); panesArray.forEach((pane, i) => { const splitbar = splitBarsArray[i]; if (splitbar && pane.splitterBarAttributes) { splitbar.htmlAttributes = pane.splitterBarAttributes; } }); elements.forEach(element => this.renderer.appendChild(this.element.nativeElement, element)); } /** * @hidden */ panes; paneChangesSubscription; _styleObserver; constructor(element, splitterService, localization, renderer, ngZone, enclosingPane) { this.element = element; this.splitterService = splitterService; this.localization = localization; this.renderer = renderer; this.ngZone = ngZone; this.enclosingPane = enclosingPane; validatePackage(packageMetadata); if (enclosingPane) { enclosingPane.containsSplitter = true; } // the handler only runs in NgZone if there are bound handlers // this line merges both streams this.layoutChange = this.splitterService.layoutChange; this.configure = this.configure.bind(this); } ngAfterContentInit() { if (!isDocumentAvailable()) { return; } this.reconfigure(); this.setFixedHeight(); const allHaveFixedSize = this.panes.length && Array.from(this.panes).every(p => p.fixedSize); if (allHaveFixedSize && isDevMode()) { throw new Error(` The Splitter should have at least one pane without a set size. See ${SIZING_DOC_LINK} for more information. `); } this._styleObserver = new MutationObserver(() => { this.ngZone.runOutsideAngular(() => { this.setFixedHeight(); }); }); this._styleObserver.observe(this.element.nativeElement, { attributeFilter: ['style'] }); } ngOnChanges(changes) { if (changes.orientation && !changes.orientation.isFirstChange()) { this.reconfigure(); } } ngOnDestroy() { if (this.enclosingPane) { this.enclosingPane.containsSplitter = false; } if (this._styleObserver) { this._styleObserver.disconnect(); this._styleObserver = null; } this.unsubscribeChanges(); } reconfigure() { this.unsubscribeChanges(); this.configure(); this.paneChangesSubscription = this.panes.changes.subscribe(this.configure); } unsubscribeChanges() { if (this.paneChangesSubscription) { this.paneChangesSubscription.unsubscribe(); this.paneChangesSubscription = null; } } configure() { this.splitterService.configure({ panes: this.panes.toArray(), orientation: this.orientation, containerSize: () => { if (this.orientation === 'vertical') { return this.element.nativeElement.clientHeight; } else { return this.element.nativeElement.clientWidth; } }, direction: this.direction }); } get direction() { return this.localization.rtl ? 'rtl' : 'ltr'; } setFixedHeight() { this.splitterService.fixedHeight = getComputedStyle(this.element.nativeElement).getPropertyValue('height') !== 'auto'; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterComponent, deps: [{ token: i0.ElementRef }, { token: i1.SplitterService }, { token: i2.LocalizationService }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: SplitterPaneComponent, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: SplitterComponent, isStandalone: true, selector: "kendo-splitter", inputs: { orientation: "orientation", splitbarWidth: "splitbarWidth", resizeStep: "resizeStep", splitterBarClass: "splitterBarClass" }, outputs: { layoutChange: "layoutChange" }, host: { properties: { "class.k-splitter": "this.hostClasses", "class.k-splitter-flex": "this.hostClasses", "class.k-splitter-horizontal": "this.horizontalHostClasses", "class.k-splitter-vertical": "this.verticalHostClasses", "attr.dir": "this.dir" } }, providers: [ SplitterService, LocalizationService, { provide: L10N_PREFIX, useValue: 'kendo.spliter' } ], queries: [{ propertyName: "panes", predicate: SplitterPaneComponent }], viewQueries: [{ propertyName: "splitbars", predicate: SplitterBarComponent, descendants: true }], exportAs: ["kendoSplitter"], usesOnChanges: true, ngImport: i0, template: ` <ng-content select="kendo-splitter-pane"></ng-content> <ng-container *ngFor=" let pane of panes; let index = index; let last = last; "> <kendo-splitter-bar kendoDraggable *ngIf="!last" [index]="index" [orientation]="orientation" [ngClass]="pane.splitterBarClass || splitterBarClass" [ngStyle]="{ width: orientation === 'horizontal' ? splitbarWidth + 'px' : undefined, height: orientation === 'vertical' ? splitbarWidth + 'px' : undefined }"> </kendo-splitter-bar> </ng-container> `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SplitterBarComponent, selector: "kendo-splitter-bar", inputs: ["orientation", "index", "htmlAttributes"] }, { kind: "directive", type: DraggableDirective, selector: "[kendoDraggable]", inputs: ["enableDrag"], outputs: ["kendoPress", "kendoDrag", "kendoRelease"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterComponent, decorators: [{ type: Component, args: [{ exportAs: 'kendoSplitter', selector: 'kendo-splitter', providers: [ SplitterService, LocalizationService, { provide: L10N_PREFIX, useValue: 'kendo.spliter' } ], template: ` <ng-content select="kendo-splitter-pane"></ng-content> <ng-container *ngFor=" let pane of panes; let index = index; let last = last; "> <kendo-splitter-bar kendoDraggable *ngIf="!last" [index]="index" [orientation]="orientation" [ngClass]="pane.splitterBarClass || splitterBarClass" [ngStyle]="{ width: orientation === 'horizontal' ? splitbarWidth + 'px' : undefined, height: orientation === 'vertical' ? splitbarWidth + 'px' : undefined }"> </kendo-splitter-bar> </ng-container> `, standalone: true, imports: [NgFor, NgIf, SplitterBarComponent, DraggableDirective, NgStyle, NgClass] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.SplitterService }, { type: i2.LocalizationService }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i3.SplitterPaneComponent, decorators: [{ type: Optional }, { type: Host }, { type: Inject, args: [SplitterPaneComponent] }] }]; }, propDecorators: { orientation: [{ type: Input }], splitbarWidth: [{ type: Input }], resizeStep: [{ type: Input }], splitterBarClass: [{ type: Input }], layoutChange: [{ type: Output }], hostClasses: [{ type: HostBinding, args: ['class.k-splitter'] }, { type: HostBinding, args: ['class.k-splitter-flex'] }], horizontalHostClasses: [{ type: HostBinding, args: ['class.k-splitter-horizontal'] }], verticalHostClasses: [{ type: HostBinding, args: ['class.k-splitter-vertical'] }], dir: [{ type: HostBinding, args: ['attr.dir'] }], splitbars: [{ type: ViewChildren, args: [SplitterBarComponent] }], panes: [{ type: ContentChildren, args: [SplitterPaneComponent] }] } });