@hug/ngx-splitter
Version:
HUG Angular - splitter component
222 lines (217 loc) • 13.4 kB
JavaScript
import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
import * as i0 from '@angular/core';
import { Directive, HostBinding, Input, EventEmitter, inject, ElementRef, ChangeDetectorRef, DestroyRef, Component, ViewEncapsulation, ChangeDetectionStrategy, Output, ContentChildren } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subject, filter, switchMap, of, fromEvent, shareReplay, mergeWith, map, tap, takeUntil, take } from 'rxjs';
/**
* Created by rtr on 22.12.2016.
*/
/**
* Directive representing a panel in a Splitter Component
*/
class NgxSplitAreaDirective {
elementRef;
order;
_size = null;
direction = 'horizontal';
/**
* Size in percent of the current area
*/
set size(value) {
this._size = coerceNumberProperty(value);
}
get size() {
const parentElement = this.elementRef.nativeElement.parentElement;
const parentSizeInPixels = parentElement && (this.direction === 'horizontal' ? parentElement.offsetWidth : parentElement.offsetHeight) || undefined;
return parentSizeInPixels && (100 * this.sizeinPixels / parentSizeInPixels);
}
get sizeinPixels() {
return this.direction === 'horizontal' ? this.elementRef.nativeElement.offsetWidth : this.elementRef.nativeElement.offsetHeight;
}
/**
* Min size in percent of the current area
*/
set minSizePixel(value) {
this._minSizePixel = coerceNumberProperty(value);
}
get minSizePixel() {
return this._minSizePixel;
}
get minWidth() {
return this.direction === 'vertical' ? undefined : this._minSizePixel;
}
get minHeight() {
return this.direction === 'horizontal' ? undefined : this._minSizePixel;
}
_minSizePixel = 0;
constructor(elementRef) {
this.elementRef = elementRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: NgxSplitAreaDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.4", type: NgxSplitAreaDirective, isStandalone: true, selector: "ngx-split-area", inputs: { size: "size", minSizePixel: "minSizePixel" }, host: { properties: { "style.order": "this.order", "style.flex-basis.%": "this._size", "style.min-width.px": "this.minWidth", "style.min-height.px": "this.minHeight" } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: NgxSplitAreaDirective, decorators: [{
type: Directive,
args: [{
selector: 'ngx-split-area',
standalone: true
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { order: [{
type: HostBinding,
args: ['style.order']
}], _size: [{
type: HostBinding,
args: ['style.flex-basis.%']
}], size: [{
type: Input
}], minSizePixel: [{
type: Input
}], minWidth: [{
type: HostBinding,
args: ['style.min-width.px']
}], minHeight: [{
type: HostBinding,
args: ['style.min-height.px']
}] } });
/**
* Splitter Component for Angular
*
* The splitter component allows to split horizontally or vertically, a container in N resizable part.
*/
class NgxSplitterComponent {
/**
* Event triggered when the user start to drag the cursor
*/
dragStart = new EventEmitter(false);
/**
* Event triggered during the cursor's drag
*/
dragProgress = new EventEmitter(false);
/**
* Event triggered when the user stop to drag the cursor
*/
dragEnd = new EventEmitter(false);
/**
* Direction of the split
* Can be `horizontal` or `vertical`
*/
set direction(direction) {
this._direction = direction;
this.ensureDirections();
}
get direction() {
return this._direction;
}
/**
* Size of the gutter in pixels
* By default `10px`
*/
set gutterSize(gutterSize) {
this._gutterSize = coerceNumberProperty(gutterSize);
}
get gutterSize() {
return this._gutterSize;
}
get styleFlexDirection() {
return this.direction === 'horizontal' ? 'row' : 'column';
}
set spliterAreas(spliterAreas) {
this.areas = spliterAreas.toArray();
this.areas.forEach((area, index) => area.order = 2 * index);
this.ensureDirections();
}
_direction = 'horizontal';
_disabled = null;
startDragging$ = new Subject();
areas = new Array();
_gutterSize = 10;
/** Retourne ou definit si le selecteur est desactivé. */
set disabled(value) {
this._disabled = coerceBooleanProperty(value) || null;
}
get disabled() {
return this._disabled;
}
elementRef = inject(ElementRef);
changeDetectorRef = inject(ChangeDetectorRef);
destroyRef = inject(DestroyRef);
constructor() {
this.startDragging$.pipe(filter(() => !this.disabled), switchMap(draggingEvent => {
const areaA = this.areas.find(a => a.order === draggingEvent.index - 1);
const areaB = this.areas.find(a => a.order === draggingEvent.index + 1);
const mouseEvent = draggingEvent.event;
if (!areaA || !areaB) {
return of(mouseEvent);
}
const startPos = this.direction === 'horizontal' ? mouseEvent.pageX || mouseEvent.screenX : mouseEvent.pageY || mouseEvent.screenY;
const containerSizeInPixels = this.direction === 'horizontal' ? this.elementRef.nativeElement.offsetWidth : this.elementRef.nativeElement.offsetHeight;
const startSizeInPixelsA = areaA.sizeinPixels;
const startSizeInPixelsB = areaB.sizeinPixels;
this.dragStart.emit();
const mouseMove$ = fromEvent(document, 'mousemove').pipe(shareReplay({ bufferSize: 1, refCount: true }));
const stopDragging$ = mouseMove$.pipe(filter(event => event.buttons !== 1), mergeWith(fromEvent(document, 'mouseup'), fromEvent(document, 'touchend'), fromEvent(document, 'touchcancel')), map(event => {
this.dragEnd.emit();
return event;
}), shareReplay({ bufferSize: 1, refCount: true }));
const drag$ = mouseMove$.pipe(filter(event => event.buttons === 1), mergeWith(fromEvent(document, 'touchmove')), tap(event => {
const pos = this.direction === 'horizontal' ? event.pageX || event.screenX : event.pageY || event.screenY;
const diffInPixels = startPos - pos;
const areaSizeInPixelsA = startSizeInPixelsA - diffInPixels;
const areaSizeInPixelsB = startSizeInPixelsB + diffInPixels;
const areaSizeInPercentA = areaA.size = Math.min(100, Math.max(0, 100 * areaSizeInPixelsA / containerSizeInPixels));
areaB.size = Math.min(100, Math.max(0, 100 * areaSizeInPixelsB / containerSizeInPixels));
const percentWithTwoDigits = Math.round(areaSizeInPercentA * 100) / 100;
this.dragProgress.emit(percentWithTwoDigits);
}), takeUntil(stopDragging$));
return stopDragging$.pipe(take(1), mergeWith(drag$));
}), takeUntilDestroyed(this.destroyRef)).subscribe(event => {
if (event.type !== 'mouseup' && event.type !== 'touchend' && event.type !== 'touchcancel') {
this.elementRef.nativeElement.setAttribute('splitting', 'true');
}
else {
this.elementRef.nativeElement.removeAttribute('splitting');
}
event.preventDefault();
this.changeDetectorRef.markForCheck();
return false;
});
}
ensureDirections() {
this.areas.forEach(area => area.direction = this.direction);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: NgxSplitterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.4", type: NgxSplitterComponent, isStandalone: true, selector: "ngx-splitter", inputs: { direction: "direction", gutterSize: "gutterSize", disabled: "disabled" }, outputs: { dragStart: "dragStart", dragProgress: "dragProgress", dragEnd: "dragEnd" }, host: { properties: { "style.flex-direction": "this.styleFlexDirection", "attr.direction": "this._direction", "attr.disabled": "this._disabled" } }, queries: [{ propertyName: "spliterAreas", predicate: NgxSplitAreaDirective }], ngImport: i0, template: "<ng-content></ng-content>\n@for (area of areas; track $index; let index = $index, last = $last) {\n @if (last === false) {\n <div\n class=\"ngx-split-gutter\"\n [style.order]=\"index * 2 + 1\"\n [style.flex-basis.px]=\"gutterSize\"\n (mousedown)=\"startDragging$.next({ event: $event, index: index * 2 + 1 })\"\n (touchstart)=\"startDragging$.next({ event: $event, index: index * 2 + 1 })\"></div>\n }\n}\n", styles: ["ngx-splitter{display:flex;flex-wrap:nowrap;justify-content:flex-start;overflow:hidden}ngx-splitter[splitting] *{-moz-user-select:none;-webkit-user-select:none;user-select:none}ngx-splitter ngx-split-area{flex:1 1 auto;overflow:hidden}ngx-splitter .ngx-split-gutter{flex-shrink:0;flex-grow:0;background-position:50%;background-repeat:no-repeat;content:none;cursor:default;background-image:none}ngx-splitter[direction=horizontal] ngx-split-area{width:100%}ngx-splitter[direction=horizontal]:not([disabled]) .ngx-split-gutter{cursor:col-resize;content:\" \";background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==)}ngx-splitter[direction=vertical] ngx-split-area{height:100%}ngx-splitter[direction=vertical]:not([disabled]) .ngx-split-gutter{cursor:row-resize;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: NgxSplitterComponent, decorators: [{
type: Component,
args: [{ encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-splitter', standalone: true, template: "<ng-content></ng-content>\n@for (area of areas; track $index; let index = $index, last = $last) {\n @if (last === false) {\n <div\n class=\"ngx-split-gutter\"\n [style.order]=\"index * 2 + 1\"\n [style.flex-basis.px]=\"gutterSize\"\n (mousedown)=\"startDragging$.next({ event: $event, index: index * 2 + 1 })\"\n (touchstart)=\"startDragging$.next({ event: $event, index: index * 2 + 1 })\"></div>\n }\n}\n", styles: ["ngx-splitter{display:flex;flex-wrap:nowrap;justify-content:flex-start;overflow:hidden}ngx-splitter[splitting] *{-moz-user-select:none;-webkit-user-select:none;user-select:none}ngx-splitter ngx-split-area{flex:1 1 auto;overflow:hidden}ngx-splitter .ngx-split-gutter{flex-shrink:0;flex-grow:0;background-position:50%;background-repeat:no-repeat;content:none;cursor:default;background-image:none}ngx-splitter[direction=horizontal] ngx-split-area{width:100%}ngx-splitter[direction=horizontal]:not([disabled]) .ngx-split-gutter{cursor:col-resize;content:\" \";background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==)}ngx-splitter[direction=vertical] ngx-split-area{height:100%}ngx-splitter[direction=vertical]:not([disabled]) .ngx-split-gutter{cursor:row-resize;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC)}\n"] }]
}], ctorParameters: () => [], propDecorators: { dragStart: [{
type: Output
}], dragProgress: [{
type: Output
}], dragEnd: [{
type: Output
}], direction: [{
type: Input
}], gutterSize: [{
type: Input
}], styleFlexDirection: [{
type: HostBinding,
args: ['style.flex-direction']
}], spliterAreas: [{
type: ContentChildren,
args: [NgxSplitAreaDirective]
}], _direction: [{
type: HostBinding,
args: ['attr.direction']
}], _disabled: [{
type: HostBinding,
args: ['attr.disabled']
}], disabled: [{
type: Input
}] } });
/**
* Generated bundle index. Do not edit.
*/
export { NgxSplitAreaDirective, NgxSplitterComponent };
//# sourceMappingURL=hug-ngx-splitter.mjs.map