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