@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
206 lines (205 loc) • 8.06 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 { EventEmitter, NgZone, Injectable } from '@angular/core';
import { isDocumentAvailable, Keys } from '@progress/kendo-angular-common';
import { shouldTogglePrev, shouldToggleNext } from './util';
import * as i0 from "@angular/core";
const MAX_PANE_HEIGHT = 33554400;
/**
* @hidden
*/
export class SplitterService {
zone;
panes;
splitterBars;
layoutChange = new EventEmitter();
resizeStep = 10;
fixedHeight;
constructor(zone) {
this.zone = zone;
}
tryToggle(paneIndex) {
const pane = this.pane(paneIndex);
if (pane.collapsible) {
pane.collapsed = !pane.collapsed;
pane.collapsedChange.emit(pane.collapsed);
this.emit(this.layoutChange, {});
if (pane.collapsed) {
pane.detectChanges();
}
}
const notCollapsed = this.panes.filter(p => !p.collapsed);
const allHaveFixedSize = notCollapsed.every(p => p.fixedSize);
notCollapsed.filter(p => p.fixedSize).forEach(pane => {
pane.forceExpand = allHaveFixedSize ? true : false;
});
return pane.collapsible;
}
togglePane(keyCode, index) {
const prev = this.pane(index);
const next = this.pane(index + 1);
if (shouldTogglePrev(keyCode, prev, next)) {
this.tryToggle(index);
}
else if (shouldToggleNext(keyCode, prev, next)) {
this.tryToggle(index + 1);
}
}
resizePane(keyCode, index) {
const state = this.dragState(index);
const direction = keyCode === Keys.ArrowLeft || keyCode === (this.rtl ? Keys.ArrowDown : Keys.ArrowUp);
let step = direction ? (-this.resizeStep) : this.resizeStep;
if (this.rtl) {
step = -step;
}
this.setSize(state, step);
}
toggleContentOverlay(index, show) {
this.pane(index).toggleOverlay(show);
this.pane(index + 1).toggleOverlay(show);
}
dragState(splitbarIndex) {
const prev = this.pane(splitbarIndex);
const next = this.pane(splitbarIndex + 1);
const total = prev.computedSize + next.computedSize;
const px = s => this.toPixels(s);
return {
prev: {
index: splitbarIndex,
initialSize: prev.computedSize,
min: px(prev.min) || total - px(next.max) || 0,
max: px(prev.max) || total - px(next.min) || total
},
next: {
index: splitbarIndex + 1,
initialSize: next.computedSize,
min: px(next.min) || total - px(prev.max) || 0,
max: px(next.max) || total - px(prev.min) || total
}
};
}
setSize(state, delta) {
const clamp = (min, max, v) => Math.min(max, Math.max(min, v));
const resize = (paneState, change, modifyMax = false) => {
const pane = this.pane(paneState.index);
const splitterSize = this.containerSize();
const newSize = clamp(paneState.min, modifyMax ? MAX_PANE_HEIGHT : paneState.max, paneState.initialSize + change);
let size = "";
if (this.isPercent(pane.size)) {
size = (100 * newSize / splitterSize) + "%";
}
else {
size = newSize + "px";
}
pane.size = size;
pane.isResized = true;
this.emit(pane.sizeChange, size);
};
const prevPane = this.pane(state.prev.index);
const nextPane = this.pane(state.next.index);
const canResizeBothPanes = this.panes.length > 2;
const modifyPrevMax = prevPane.orientation === 'vertical' && !this.fixedHeight && !prevPane.max;
const modifyNextMax = prevPane.orientation === 'vertical' && !this.fixedHeight && !nextPane.max;
if (prevPane.fixedSize && nextPane.fixedSize || canResizeBothPanes) {
const bothVertical = prevPane.orientation === 'vertical' && nextPane.orientation === 'vertical';
if (bothVertical) {
if (modifyNextMax) {
resize(state.prev, delta, modifyPrevMax);
}
else if (modifyPrevMax) {
resize(state.next, -delta, modifyNextMax);
}
else {
resize(state.prev, delta, modifyNextMax);
resize(state.next, -delta, modifyPrevMax);
}
}
else {
resize(state.prev, delta);
resize(state.next, -delta);
}
}
else if (nextPane.fixedSize || nextPane.collapsible) {
resize(state.next, -delta, modifyNextMax);
}
else {
resize(state.prev, delta, modifyPrevMax);
}
this.emit(this.layoutChange, {});
}
isDraggable(splitBarIndex) {
const prev = this.pane(splitBarIndex);
const next = this.pane(splitBarIndex + 1);
const betweenResizablePanes = prev?.resizable && next?.resizable;
const nearCollapsedPane = prev?.collapsed || next?.collapsed;
return betweenResizablePanes && !nearCollapsedPane;
}
isStatic(splitBarIndex) {
const prev = this.pane(splitBarIndex);
const next = this.pane(splitBarIndex + 1);
const betweenResizablePanes = prev?.resizable && next?.resizable;
const nearCollapsiblePane = prev?.collapsible || next?.collapsible;
return !betweenResizablePanes && !nearCollapsiblePane;
}
pane(index) {
if (!isDocumentAvailable()) {
return;
}
if (!this.panes) {
throw new Error("Panes not initialized");
}
if (index < 0 || index >= this.panes.length) {
throw new Error("Index out of range");
}
return this.panes[index];
}
paneByIndex(pane) {
if (!this.panes) {
return -1;
}
return this.panes.findIndex(splitterPane => splitterPane === pane);
}
getPaneSplitterBar(pane) {
if (!this.splitterBars) {
return;
}
const paneIndex = this.paneByIndex(pane);
if (paneIndex < 0 || paneIndex >= this.splitterBars.length) {
return null;
}
return this.splitterBars[paneIndex];
}
configure({ panes, orientation, containerSize, direction }) {
this.panes = panes;
this.panes.forEach((pane, index) => {
pane.order = index * 2;
pane.orientation = orientation;
});
this.containerSize = containerSize;
this.rtl = direction === 'rtl';
}
containerSize = () => { };
rtl;
isPercent(size) {
return /%$/.test(size);
}
toPixels(size) {
let result = parseFloat(size);
if (this.isPercent(size)) {
result = (this.containerSize() * result / 100);
}
return result;
}
emit(emitter, args) {
if (emitter.observers.length) {
this.zone.run(() => emitter.emit(args));
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.NgZone }]; } });