import { Component, OnInit, Input, HostBinding, Renderer2, AfterViewInit, ElementRef, Output, EventEmitter, HostListener, OnDestroy } from "@angular/core"; @Component({ selector: "farris-splitter-pane", template: `
`, styles: [] }) export class SplitterPaneComponent implements OnInit, AfterViewInit, OnDestroy { @HostBinding("class.f-component-splitter-pane") klass = true; /** * 2022年6月8日 ximena * size、max、min、collapsible、sizeChange、order、orderState这些属性没有被实际使用 * 该组件常用的属性来自于 ngResizable 拖拽组件 */ @Input() set size(value: any) { this._size = value; this.sizeChange.emit(value); // this._sizeState.next(value); } @Input() max: string; @Input() min: string; @Input() collapsible: boolean = false; @Output() sizeChange = new EventEmitter(); _size: string; //_sizeState = new BehaviorSubject({}); el: HTMLElement; set order(value: number) { this._order = value; //this.orderState.next(value); } _order: number; //orderState = new BehaviorSubject({}); // 记录収折前宽度,方便后续恢复 private _beforeCollapseDistance = -1; private _enableCollapse = false; // 启用収折 @Input() set enableCollapse(value) { if (value !== undefined && value !== null) { this._enableCollapse = value; // 变化属性时 if (this._enableCollapse && this.initCollapse) { this.toggleInitCollapseState(); } } } get enableCollapse() { return this._enableCollapse; } // 处理关联变动中,另外一处有iframe,iframe影响拖拽的性能 private _relatedIframeParent: string | ElementRef | any; // 记录真实的iframe父容器 private _relatedContainer = null; // 标记iframe父容器的样式属性position的值 private _relatedContainerPosition = ""; // 记录iframe在拖拽时的遮罩层 private _relatedIframeOverlay = null; // 标记iframe遮罩层是否已显示 private _iframeOverlayShownFlag = false; @Input() set relatedIframeParent(value: string | ElementRef | any) { this._relatedIframeParent=value; this.getRelatedIframeParent(value); } get relatedIframeParent() { return this._relatedContainer; } // 拖拽到某个值时,自动收起的阈值。如果阈值设置小于0,则不处理自动收起 @Input() resizeCollapseThreshold = 20; // pane在splitter的位置,left 左侧 right 右侧 top 顶部 bottom 底部 private pos = "right"; @Input() set collapsePosition(value: string) { if (this.enableCollapse && value) { // 规范position let positionCorrect = ["left", "right", "top", "bottom"].findIndex( item => item == value ); if (positionCorrect < -1) { value = "right"; } if (this.pos != value) { this.el && this.renderer.removeClass( this.el, "splitter-pane-collapse-on-" + this.pos ); this.pos = value; this.el && this.renderer.addClass( this.el, "splitter-pane-collapse-on-" + value ); } } } get collapsePosition(): string { return this.pos; } // 真实的展开时的距离, private defaultExpandDistance = "20%"; private realExpandDisatance = ""; // 展开时的距离,不设置时为空,设置了按照设置的值来 private _expandDistance = ""; @Input() set expandDistance(value: string | number) { if (value) { this._expandDistance = this.resolveSize(value + ""); } else { this._expandDistance = ""; } } get expandDistance() { return this._expandDistance; } // 初始收起状态 private initCollapse = false; @Input() set collapsed(value) { if (this.enableCollapse && value !== undefined && value !== null) { if (this.initCollapse !== value) { this.initCollapse = value; this.toggleInitCollapseState(); } } } get collapsed() { return this.initCollapse; } /**收起折叠状态变化 */ @Output("collapseStateChange") collapseStateChange = new EventEmitter(); // collapse的动画样式名 private animateCls = "splitter-pane-collapse-animate"; // 标记是否需要更新变量 private updateParamsFlag = false; // 标记拖拽时上次的大小 private resizeDistance = -1; /** * 拖拽的变化中事件 * 如果超过阈值,不处理 * 暂未处理拖拽最大、最小值的问题 */ @HostListener("rzResizing", ["$event"]) onRZResizing(pos) { if (this.enableCollapse && pos && pos["size"]) { let changeProp = this.getStyleProp(); let resizeDirection = ""; if (this.resizeDistance >= 0) { // 开始记录后 // 此次值比上次记录大,说明在expand resizeDirection = pos["size"][changeProp] > this.resizeDistance ? "expand" : "collapse"; } this.resizeDistance = pos["size"][changeProp]; if (resizeDirection == "expand" && this.initCollapse) { this.initCollapse = false; } if ( pos["size"][changeProp] > this.resizeCollapseThreshold || !resizeDirection ) { // 拖拽速度快的时候,会漏掉计算的时机 return; } if (this.resizeCollapseThreshold > 0) { // 设置了自动収折阈值 if (!this.initCollapse && resizeDirection == "collapse") { // 处理拖拽収折过程中,自动收起;不处理展开过程中的,自动展开 if ( pos["actions"] && pos["actions"].hasOwnProperty("stopResize") ) { pos["actions"]["stopResize"](); } this.toggleCollapseState(null); } } else { // 未设置自动収折阈值,需要单独处理 即将收起时状态、即将展开时状态 if (resizeDirection == "collapse") { if (pos["size"][changeProp] < 3 && !this.initCollapse) { this.initCollapse = true; } } } } } // 拖拽的开始的事件 @HostListener("rzStart", ["$event"]) onRZStart(pos) { if (this.enableCollapse) { if (this.el.className.indexOf(this.animateCls) >-1) { this.renderer.removeClass(this.el, this.animateCls); } } // 如果有iframe遮罩并且没有显示 if (this._relatedIframeOverlay && !this._iframeOverlayShownFlag) { if ( ["relative", "absolute"].indexOf( this._relatedContainerPosition ) < 0 ) { this.renderer.setStyle( this._relatedContainer, "position", "relative" ); } this.renderer.setStyle( this._relatedIframeOverlay, "display", "block" ); this._iframeOverlayShownFlag = true; } if ( !this.updateParamsFlag && pos["actions"] && pos["actions"].hasOwnProperty("updateParams") ) { pos["actions"]["updateParams"]({ fixedEdge: true }); this.updateParamsFlag = true; } } // 拖拽的停止事件 @HostListener("rzStop", ["$event"]) onRZStop(pos) { this.resizeDistance = -1; // 如果有iframe遮罩并且显示了 if (this._relatedIframeOverlay && this._iframeOverlayShownFlag) { if ( ["relative", "absolute"].indexOf( this._relatedContainerPosition ) < 0 ) { this.renderer.setStyle( this._relatedContainer, "position", this._relatedContainerPosition ); } this.renderer.setStyle( this._relatedIframeOverlay, "display", "none" ); this._iframeOverlayShownFlag = false; } } constructor(private elementRef: ElementRef, private renderer: Renderer2) { // 兼容旧表单写法 if(this.elementRef){ this.el = this.elementRef.nativeElement; } } ngOnInit() { if(this.elementRef){ this.compatibleEl(); } } ngAfterViewInit() { if(!this.el){ this.compatibleEl(); } } /** * 兼容旧表单写法 */ private compatibleEl(){ this.el = this.elementRef.nativeElement; // 设置了iframe容器,但是没有创建遮罩 if (this._relatedIframeParent && !this._relatedIframeOverlay) { this.getRelatedIframeParent(this._relatedIframeParent); } this.enableCollapse && this.renderer.addClass( this.el, "splitter-pane-collapse-on-" + this.collapsePosition ); } ngOnDestroy() {} /*宽高类型是 string或者number 解析宽高 尺寸 */ resolveSize(size) { const regex = /px|%|rem|em/; // 说明是字符串 return regex.test(size) ? `${parseInt(size, 10)}${size.match(regex)[0]}` : `${size}px`; } /** * 规范获取iframe所在的父容器 */ private getRelatedIframeParent(content) { if (!content||!this.el) { this._relatedContainer = null; } if (content instanceof ElementRef) { this._relatedContainer = content.nativeElement; } else if (typeof content == "string") { this._relatedContainer = this.el.parentElement.querySelector( content ); } else { this._relatedContainer = content; } if (this._relatedContainer) { this._relatedContainerPosition = this._relatedContainer["style"][ "position" ]; // 构造遮罩层 this._relatedIframeOverlay = this.renderer.createElement("div"); this._relatedIframeOverlay.className = "f-utils-absolute-all"; this.renderer.setStyle( this._relatedIframeOverlay, "display", "none" ); this.renderer.appendChild( this._relatedContainer, this._relatedIframeOverlay ); } } /** * 点击収折 */ toggleCollapseState(ev: MouseEvent) { //禁止冒泡 ev && ev.stopPropagation(); if (!this.el) return; if (this.initCollapse) { // 待展开 this.renderer.addClass(this.el, this.animateCls); this.changeCollapse("expand"); } else { // 待收起 this.renderer.addClass(this.el, this.animateCls); this.changeCollapse("collapse"); } this.initCollapse = !this.initCollapse; this.collapseStateChange.emit(this.initCollapse); } /** * 切换初始收起状态 */ toggleInitCollapseState() { if (!this.el) return; if (this.initCollapse) { // 收起 this.changeCollapse("collapse"); } else { // 展开 this.changeCollapse("expand"); } this.collapseStateChange.emit(this.initCollapse); } /** * expand 为展开,collapse 为收起 * @param collapseOrExpand */ private changeCollapse(collapseOrExpand: string) { let changeProp = this.getStyleProp(); switch (collapseOrExpand) { case "expand": // 展开 // 如果存在手写width赋值给pane会有问题 if (this._expandDistance) { //设置了展开距离 this.realExpandDisatance = this._expandDistance; } else { //没有设置,按照之前的记录,如果存在就按照记录的值,如果没有就按照默认的值 this.realExpandDisatance = this._beforeCollapseDistance ? this._beforeCollapseDistance + "px" : this.defaultExpandDistance; } this.renderer.setStyle( this.el, changeProp, this.realExpandDisatance ); break; default: // 收起 this._beforeCollapseDistance = changeProp == "height" ? this.el.offsetHeight : this.el.offsetWidth; this.renderer.setStyle(this.el, changeProp, "0px"); } } /** * 布局排列不一样,影响到是设置宽度还是高度 */ private getStyleProp() { let hori = ["left", "right"].findIndex( item => item == this.collapsePosition ); return hori > -1 ? "width" : "height"; } }