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";
}
}