From 1a788eb8ed2a0506b6d4b424aa36edb0cfe58d4a Mon Sep 17 00:00:00 2001 From: yigitfindikli Date: Tue, 22 Dec 2020 01:04:12 +0300 Subject: [PATCH] Fixed #9677 - Splitter Component --- src/app/components/splitter/splitter.css | 4 + src/app/components/splitter/splitter.ts | 266 +++++++++++++++--- .../components/splitter/splitterdemo.html | 61 ++++ 3 files changed, 296 insertions(+), 35 deletions(-) diff --git a/src/app/components/splitter/splitter.css b/src/app/components/splitter/splitter.css index 38593486fb1..86c54259949 100644 --- a/src/app/components/splitter/splitter.css +++ b/src/app/components/splitter/splitter.css @@ -15,6 +15,10 @@ display: flex; } +.p-splitter-panel p-splitter { + flex-grow: 1; +} + .p-splitter-panel .p-splitter { flex-grow: 1; border: 0 none; diff --git a/src/app/components/splitter/splitter.ts b/src/app/components/splitter/splitter.ts index 4d83b9bf050..d22e38d63b3 100755 --- a/src/app/components/splitter/splitter.ts +++ b/src/app/components/splitter/splitter.ts @@ -1,36 +1,56 @@ -import { NgModule, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, ContentChildren, QueryList, ElementRef, Inject, forwardRef, ChangeDetectorRef, TemplateRef, Directive } from '@angular/core'; +import { NgModule, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, ContentChildren, QueryList, ElementRef, ChangeDetectorRef, TemplateRef, ViewChild, Output, EventEmitter } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { DomHandler } from '../dom/domhandler'; -import { Subscription } from 'rxjs'; +import { DomHandler } from 'primeng/dom'; @Component({ selector: 'p-splitter', template: ` -
- +
+ +
+ +
+
+
+
+
`, - changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, - styleUrls: ['./splitter.css'] + changeDetection: ChangeDetectionStrategy.OnPush, + styleUrls: ['./splitter.css'], + host: { + '[class.p-splitter-panel-nested]': 'nested' + } }) export class Splitter { @Input() styleClass: string; + @Input() panelStyleClass: string; + @Input() style: any; @Input() stateStorage: string = "session"; - @Input() stateKey: string; + @Input() stateKey: string = null; @Input() layout: string = "horizontal"; @Input() gutterSize: number = 4; - // @ContentChildren(SplitterPanel) panelList: QueryList; + @Input() panelSizes: number[] = []; + + @Input() minSizes: number[] = []; - // panels: SplitterPanel[] = []; + @Output() onResizeEnd: EventEmitter = new EventEmitter(); + + @ContentChildren(TemplateRef) panels: QueryList; + + @ViewChild('container', { static: false }) containerViewChild: ElementRef; + + nested = false; dragging = false; @@ -52,39 +72,203 @@ export class Splitter { prevPanelSize = null; - panelSizes = null; + _panelSizes = null; prevPanelIndex = null; - panelListSubscription: Subscription; - constructor(public cd: ChangeDetectorRef, private el: ElementRef) { } ngOnInit() { + this.nested = this.isNested(); } - ngAfterContentInit() { - // this.panels = this.panelList.toArray(); + ngAfterViewInit() { + if (this.panels && this.panels.length) { + let initialized = false; + if (this.isStateful()) { + initialized = this.restoreState(); + } + + if (!initialized) { + let children = [...this.el.nativeElement.children[0].children].filter(child => DomHandler.hasClass(child, 'p-splitter-panel')); + let _panelSizes = []; + + this.panels.map((panel, i) => { + let panelInitialSize = this.panelSizes.length -1 >= i ? this.panelSizes[i]: null; + let panelSize = panelInitialSize || (100 / this.panels.length); + _panelSizes[i] = panelSize; + children[i].style.flexBasis = 'calc(' + panelSize + '% - ' + ((this.panels.length - 1) * this.gutterSize) + 'px)'; + }); + + this._panelSizes = _panelSizes; + } + } + } + + onResizeStart(event, index) { + this.gutterElement = event.currentTarget; + this.size = this.horizontal() ? DomHandler.getWidth(this.containerViewChild.nativeElement) : DomHandler.getHeight(this.containerViewChild.nativeElement); + this.dragging = true; + this.startPos = this.horizontal() ? event.pageX : event.pageY; + this.prevPanelElement = this.gutterElement.previousElementSibling; + this.nextPanelElement = this.gutterElement.nextElementSibling; + this.prevPanelSize = 100 * (this.horizontal() ? DomHandler.getOuterWidth(this.prevPanelElement, true): DomHandler.getOuterHeight(this.prevPanelElement, true)) / this.size; + this.nextPanelSize = 100 * (this.horizontal() ? DomHandler.getOuterWidth(this.nextPanelElement, true): DomHandler.getOuterHeight(this.nextPanelElement, true)) / this.size; + this.prevPanelIndex = index; + DomHandler.addClass(this.gutterElement, 'p-splitter-gutter-resizing'); + DomHandler.addClass(this.containerViewChild.nativeElement, 'p-splitter-resizing'); + } - // if (this.panels && this.panels.length) { - // let initialized = false; - // if (!initialized) { - // let children = [...this.panels].filter(child => DomHandler.hasClass(child.el.nativeElement.children[0], 'p-splitter-panel')); - // let _panelSizes = []; + onResize(event) { + let newPos; + if (this.horizontal()) + newPos = (event.pageX * 100 / this.size) - (this.startPos * 100 / this.size); + else + newPos = (event.pageY * 100 / this.size) - (this.startPos * 100 / this.size); + + let newPrevPanelSize = this.prevPanelSize + newPos; + let newNextPanelSize = this.nextPanelSize - newPos; + + if (this.validateResize(newPrevPanelSize, newNextPanelSize)) { + this.prevPanelElement.style.flexBasis = 'calc(' + newPrevPanelSize + '% - ' + ((this.panels.length - 1) * this.gutterSize) + 'px)'; + this.nextPanelElement.style.flexBasis = 'calc(' + newNextPanelSize + '% - ' + ((this.panels.length - 1) * this.gutterSize) + 'px)'; + this._panelSizes[this.prevPanelIndex] = newPrevPanelSize; + this._panelSizes[this.prevPanelIndex + 1] = newNextPanelSize; + } + } - // this.panels.map((panel, i) => { - // let panelInitialSize = panel && panel.size ? panel.size : null; - // let panelSize = panelInitialSize || (100 / this.panels.length); - // _panelSizes[i] = panelSize; - // children[i].style.flexBasis = 'calc(' + panelSize + '% - ' + ((this.panels.length - 1) * this.gutterSize) + 'px)'; - // children[i]; - // }); + resizeEnd(event) { + if (this.isStateful()) { + this.saveState(); + } + + this.onResizeEnd.emit({originalEvent: event, sizes: this._panelSizes}) + DomHandler.removeClass(this.gutterElement, 'p-splitter-gutter-resizing'); + DomHandler.removeClass(this.containerViewChild.nativeElement, 'p-splitter-resizing'); + this.clear(); + } - // this.panelSizes = _panelSizes; - // } - // } + onGutterMouseDown(event, index) { + this.onResizeStart(event, index); + this.bindMouseListeners(); + } - this.cd.markForCheck(); + onGutterTouchStart(event, index) { + this.onResizeStart(event, index); + event.preventDefault(); + } + + onGutterTouchMove(event) { + this.onResize(event); + event.preventDefault(); + } + + onGutterTouchEnd(event) { + this.resizeEnd(event); + event.preventDefault(); + } + + validateResize(newPrevPanelSize, newNextPanelSize) { + if (this.minSizes.length >= 1 && this.minSizes[0] && this.minSizes[0] > newPrevPanelSize) { + return false; + } + + if (this.minSizes.length > 1 && this.minSizes[1] && this.minSizes[1] > newNextPanelSize) { + return false; + } + + return true; + } + + bindMouseListeners() { + if (!this.mouseMoveListener) { + this.mouseMoveListener = event => this.onResize(event) + document.addEventListener('mousemove', this.mouseMoveListener); + } + + if (!this.mouseUpListener) { + this.mouseUpListener = event => { + this.resizeEnd(event); + this.unbindMouseListeners(); + } + document.addEventListener('mouseup', this.mouseUpListener); + } + } + + unbindMouseListeners() { + if (this.mouseMoveListener) { + document.removeEventListener('mousemove', this.mouseMoveListener); + this.mouseMoveListener = null; + } + + if (this.mouseUpListener) { + document.removeEventListener('mouseup', this.mouseUpListener); + this.mouseUpListener = null; + } + } + + clear() { + this.dragging = false; + this.size = null; + this.startPos = null; + this.prevPanelElement = null; + this.nextPanelElement = null; + this.prevPanelSize = null; + this.nextPanelSize = null; + this.gutterElement = null; + this.prevPanelIndex = null; + } + + isNested() { + if (this.el.nativeElement) { + let parent = this.el.nativeElement.parentElement; + while (parent && !DomHandler.hasClass(parent, 'p-splitter')) { + parent = parent.parentElement; + } + + return parent !== null; + } + else { + return false; + } + } + + isStateful() { + return this.stateKey != null; + } + + getStorage() { + switch(this.stateStorage) { + case 'local': + return window.localStorage; + + case 'session': + return window.sessionStorage; + + default: + throw new Error(this.stateStorage + ' is not a valid value for the state storage, supported values are "local" and "session".'); + } + } + + saveState() { + this.getStorage().setItem(this.stateKey, JSON.stringify(this._panelSizes)); + } + + restoreState() { + const storage = this.getStorage(); + const stateString = storage.getItem(this.stateKey); + + if (stateString) { + this._panelSizes = JSON.parse(stateString); + let children = [...this.containerViewChild.nativeElement.children].filter(child => DomHandler.hasClass(child, 'p-splitter-panel')); + children.forEach((child, i) => { + child.style.flexBasis = 'calc(' + this._panelSizes[i] + '% - ' + ((this.panels.length - 1) * this.gutterSize) + 'px)'; + }); + + return true; + } + + return false; } containerClass() { @@ -95,10 +279,22 @@ export class Splitter { }; } - ngOnDestroy() { - if (this.panelListSubscription) { - this.panelListSubscription.unsubscribe(); - } + panelContainerClass() { + return { + 'p-splitter-panel': true, + 'p-splitter-panel-nested': true + }; + } + + gutterStyle() { + if (this.horizontal()) + return {width: this.gutterSize + 'px'}; + else + return {height: this.gutterSize + 'px'}; + } + + horizontal() { + return this.layout === 'horizontal'; } } diff --git a/src/app/showcase/components/splitter/splitterdemo.html b/src/app/showcase/components/splitter/splitterdemo.html index eeabf6330e0..a936ba3eb22 100644 --- a/src/app/showcase/components/splitter/splitterdemo.html +++ b/src/app/showcase/components/splitter/splitterdemo.html @@ -7,7 +7,68 @@

Splitter

+
Horizontal
+ +
+ Panel 1 +
+
+ +
+ Panel 2 +
+
+
+
+ +
+
Vertical
+ + +
+ Panel 1 +
+
+ +
+ Panel 2 +
+
+
+
+ +
+
Nested
+ + +
+ Panel 1 +
+
+ + + +
+ Panel 2 +
+
+ + + +
+ Panel 3 +
+
+ +
+ Panel 4 +
+
+
+
+
+