UNPKG

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