ngx-split-view
Version:
A resizable flex-box split layout component for Angular
249 lines (243 loc) • 8.62 kB
JavaScript
import { __decorate } from 'tslib';
import { ViewContainerRef, Input, Directive, TemplateRef, EventEmitter, Output, ContentChildren, ContentChild, HostBinding, Component, ChangeDetectionStrategy, NgModule } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { startWith, switchMap, mergeAll, map } from 'rxjs/operators';
import Split from 'split.js';
let SplitPaneDirective = class SplitPaneDirective {
constructor(viewContainerRef) {
this.viewContainerRef = viewContainerRef;
this.splitRatio = 1;
this.minSize = 100;
this.sizeChanges = new Subject();
this.minSizeChanges = new Subject();
}
ngOnChanges(changes) {
if (changes.splitRatio) {
this.sizeChanges.next();
}
if (changes.minSize) {
this.minSizeChanges.next();
}
}
};
SplitPaneDirective.ctorParameters = () => [
{ type: ViewContainerRef }
];
__decorate([
Input()
], SplitPaneDirective.prototype, "splitRatio", void 0);
__decorate([
Input()
], SplitPaneDirective.prototype, "minSize", void 0);
SplitPaneDirective = __decorate([
Directive({
// tslint:disable-next-line: directive-selector
selector: '[splitPane]'
})
], SplitPaneDirective);
let SplitViewGutterDirective = class SplitViewGutterDirective {
constructor(template) {
this.template = template;
}
};
SplitViewGutterDirective.ctorParameters = () => [
{ type: TemplateRef }
];
SplitViewGutterDirective = __decorate([
Directive({
// tslint:disable-next-line: directive-selector
selector: '[gutterTemplate]'
})
], SplitViewGutterDirective);
let SplitViewComponent = class SplitViewComponent {
constructor(viewContainerRef) {
this.viewContainerRef = viewContainerRef;
this.dragging = new EventEmitter();
this.dragStart = new EventEmitter();
this.dragEnd = new EventEmitter();
this.expandToMin = false;
this.gutterSize = 10;
this.gutterAlign = 'center';
this.snapOffset = 30;
this.dragInterval = 1;
this.direction = 'horizontal';
this.subscriptions = new Subscription();
}
get isHorizontal() { return this.direction === 'horizontal'; }
get isVertical() { return this.direction === 'vertical'; }
ngOnChanges(changes) {
if (changes.direction ||
changes.gutterSize ||
changes.gutterAlign ||
changes.snapOffset ||
changes.dragInterval ||
changes.expandToMin) {
this.refresh();
}
}
ngAfterContentInit() {
const splitPaneChanges = this.splitPanes.changes
.pipe(startWith(this.splitPanes));
const childrenChanges = splitPaneChanges
.subscribe(() => this.refresh());
const sizeChanges = splitPaneChanges
.pipe(switchMap(() => this.splitPanes.map(p => p.sizeChanges)), mergeAll())
.subscribe(() => { var _a; return (_a = this.split) === null || _a === void 0 ? void 0 : _a.setSizes(this.getSizes()); });
const dragEndEvents = this.dragEnd
.pipe(map(event => event.sizes))
.subscribe(sizes => {
const panes = this.splitPanes.toArray();
for (let i = 0; i < panes.length; i++) {
panes[i].splitRatio = sizes[i];
}
});
this.subscriptions.add(childrenChanges);
this.subscriptions.add(sizeChanges);
this.subscriptions.add(dragEndEvents);
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
this.destroySplit();
}
refresh() {
this.destroySplit();
if (!this.splitPanes || this.splitPanes.length === 0) {
return;
}
const panes = new Array();
const minSizes = new Array();
this.splitPanes.forEach(d => {
panes.push(d.viewContainerRef.element.nativeElement);
minSizes.push(d.minSize);
});
const splitOptions = {
sizes: this.getSizes(),
minSize: minSizes,
expandToMin: this.expandToMin,
gutterSize: this.gutterSize,
gutterAlign: this.gutterAlign,
snapOffset: this.snapOffset,
dragInterval: this.dragInterval,
direction: this.direction,
elementStyle: (dimension, size, gutterSize) => ({
flexBasis: 'calc(' + size + '% - ' + gutterSize + 'px)'
}),
gutterStyle: (dimension, gutterSize) => ({
flexBasis: gutterSize + 'px'
}),
onDrag: (sizes) => {
if (this.dragging.observers.length > 0) {
this.dragging.emit({ source: this, sizes });
}
},
onDragStart: (sizes) => this.dragStart.emit({ source: this, sizes }),
onDragEnd: (sizes) => this.dragEnd.emit({ source: this, sizes })
};
if (this.gutterDef) {
splitOptions.gutter = (index, direction) => {
const view = this.viewContainerRef.createEmbeddedView(this.gutterDef.template);
const elements = view.rootNodes.filter(e => e instanceof HTMLElement);
if (elements.length !== 1) {
throw new Error('Gutter template must contain exactly one element');
}
return elements[0];
};
}
this.split = Split(panes, splitOptions);
}
destroySplit() {
if (!this.split) {
return;
}
this.viewContainerRef.clear();
this.split.destroy(false, !!this.gutterDef);
this.split = undefined;
}
getSizes() {
const panes = new Array();
const ratios = new Array();
let totalSize = 0;
this.splitPanes.forEach(d => {
panes.push(d.viewContainerRef.element.nativeElement);
const sanitizedSize = d.splitRatio >= 0 ? d.splitRatio : 0;
ratios.push(sanitizedSize);
totalSize += sanitizedSize;
});
return ratios.map(size => (size / totalSize) * 100);
}
};
SplitViewComponent.ctorParameters = () => [
{ type: ViewContainerRef }
];
__decorate([
Output()
], SplitViewComponent.prototype, "dragging", void 0);
__decorate([
Output()
], SplitViewComponent.prototype, "dragStart", void 0);
__decorate([
Output()
], SplitViewComponent.prototype, "dragEnd", void 0);
__decorate([
Input()
], SplitViewComponent.prototype, "expandToMin", void 0);
__decorate([
Input()
], SplitViewComponent.prototype, "gutterSize", void 0);
__decorate([
Input()
], SplitViewComponent.prototype, "gutterAlign", void 0);
__decorate([
Input()
], SplitViewComponent.prototype, "snapOffset", void 0);
__decorate([
Input()
], SplitViewComponent.prototype, "dragInterval", void 0);
__decorate([
Input()
], SplitViewComponent.prototype, "direction", void 0);
__decorate([
ContentChildren(SplitPaneDirective)
], SplitViewComponent.prototype, "splitPanes", void 0);
__decorate([
ContentChild(SplitViewGutterDirective, { static: true })
], SplitViewComponent.prototype, "gutterDef", void 0);
__decorate([
HostBinding('class.split-view-horizontal')
], SplitViewComponent.prototype, "isHorizontal", null);
__decorate([
HostBinding('class.split-view-vertical')
], SplitViewComponent.prototype, "isVertical", null);
SplitViewComponent = __decorate([
Component({
// tslint:disable-next-line: component-selector
selector: 'split-view',
template: '<ng-content select="[splitPane]"></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [":host{display:flex}:host.split-view-horizontal{flex-direction:row}:host.split-view-vertical{flex-direction:column}::ng-deep .gutter.gutter-horizontal{cursor:col-resize}::ng-deep .gutter.gutter-vertical{cursor:row-resize}"]
})
], SplitViewComponent);
let SplitViewModule = class SplitViewModule {
};
SplitViewModule = __decorate([
NgModule({
declarations: [
SplitPaneDirective,
SplitViewGutterDirective,
SplitViewComponent
],
exports: [
SplitPaneDirective,
SplitViewGutterDirective,
SplitViewComponent
]
})
], SplitViewModule);
/*
* Public API Surface of ngx-split-view
*/
/**
* Generated bundle index. Do not edit.
*/
export { SplitPaneDirective, SplitViewComponent, SplitViewGutterDirective, SplitViewModule };
//# sourceMappingURL=ngx-split-view.js.map