@rdkmaster/jigsaw-labs
Version:
Jigsaw, the next generation component set for RDK
376 lines (330 loc) • 11.6 kB
text/typescript
import {Component, Input, NgModule, Output, EventEmitter, OnInit, ElementRef, ViewChild, HostBinding} from "@angular/core";
import {AbstractJigsawComponent} from "../common";
import {CommonModule} from "@angular/common";
import {PerfectScrollbarModule} from "ngx-perfect-scrollbar";
import {CommonUtils} from "../../core/utils/common-utils";
/**
* 本组件模拟了一个类似抽屉的效果,用于收纳UI上重要性不高的视图。
*
* $since = v1.1.7
*/
export class JigsawDrawer extends AbstractJigsawComponent implements OnInit {
constructor(private _elementRef: ElementRef) {
super();
}
private _position: "left" | "right" | "top" | "bottom" = "left";
/**
* 用于设置抽屉的位置,支持上下左右4个方向。
*
* $demo = drawer/basic
*/
public get position(): "left" | "right" | "top" | "bottom" {
return this._position;
}
public set position(value: "left" | "right" | "top" | "bottom") {
if (!value) return;
this._position = value;
this._update();
this._setContainer();
}
private _open: boolean = false;
/**
* 代表了抽屉的状态,`true`为打开状态,`false`为关闭状态。在双绑模式下,改变此属性的值可以打开或者关闭抽屉。
*
* $demo = drawer/basic
*/
public get open(): boolean {
return this._open;
}
public set open(value: boolean) {
this._open = value;
this._update();
}
/**
* 当抽屉的状态发生变化时,Jigsaw发出此事件
*
* $demo = drawer/basic
*
* @type {EventEmitter<boolean>}
*/
public openChange: EventEmitter<boolean> = new EventEmitter<boolean>();
/**
* 容器的selector,支持'.className'、'#id'、'[attr]'、'tagName'
* 向上寻找离抽屉最近的匹配节点作为抽屉的容器
*
* $demo = drawer/with-div
* $demo = drawer/with-scrollbar
* $demo = drawer/with-tab
*/
public container: string;
private _offsetTop: string;
private _offsetLeft: string;
private _offsetRight: string;
private _offsetBottom: string;
/**
* $demo = drawer/with-div
*/
get offsetTop(): string {
return this._offsetTop;
}
set offsetTop(value: string) {
this._offsetTop = CommonUtils.getCssValue(value);
this._update();
}
/**
* $demo = drawer/with-div
*/
get offsetLeft(): string {
return this._offsetLeft;
}
set offsetLeft(value: string) {
this._offsetLeft = CommonUtils.getCssValue(value);
this._update();
}
/**
* $demo = drawer/with-div
*/
get offsetRight(): string {
return this._offsetRight;
}
set offsetRight(value: string) {
this._offsetRight = CommonUtils.getCssValue(value);
this._update();
}
/**
* $demo = drawer/with-div
*/
get offsetBottom(): string {
return this._offsetBottom;
}
set offsetBottom(value: string) {
this._offsetBottom = CommonUtils.getCssValue(value);
this._update();
}
/**
* $demo = drawer/with-div
*/
public get width(): string {
return this._width;
}
public set width(value: string) {
this._width = CommonUtils.getCssValue(value);
this._update();
}
/**
* $demo = drawer/with-div
*/
public get height(): string {
return this._height;
}
public set height(value: string) {
this._height = CommonUtils.getCssValue(value);
this._update();
}
/**
* $demo = drawer/in-dom
*/
public floating: boolean = true;
private _drawerEl: ElementRef;
private _$hostWidth: string;
private _$hostHeight: string;
private _setHostSize() {
this._$hostWidth = this._calcHostWidth();
this._$hostHeight = this._calcHostHeight();
}
/**
* host(jigsaw-drawer)宽高的计算方法:
* 浮动模式下:不做任何处理。
* 文档流模式下:
* host是inline-block的模式,所以width和height属性没值时,默认按内容计算尺寸;
* 有值时则为width和height的属性值
*/
private _calcHostWidth(): string {
if (this.floating) return null;
let width = this._calcWidth();
if (this.position == "top" || this.position == "bottom") {
// 上下抽屉宽度为固定值
return width ? width : this._drawerEl.nativeElement.offsetWidth + "px";
} else if (this.open) {
return width ? width : this._drawerEl.nativeElement.offsetWidth + 14 + "px";
} else {
return "14px";
}
}
private _calcHostHeight(): string {
if (this.floating) return null;
let height = this._calcHeight();
if (this.position == "left" || this.position == "right") {
// 左右抽屉height为固定值
return height ? height : this._drawerEl.nativeElement.offsetHeight + "px";
} else if (this.open) {
return height ? height : this._drawerEl.nativeElement.offsetHeight + 14 + "px";
} else {
return "14px";
}
}
/**
* @internal
*/
public _$handleStyle = {};
private _setStyle() {
const styleTemp = this.floating ? {
position: this.container ? 'absolute' : 'fixed',
left: (this.position == "top" || this.position == "bottom") && this.offsetLeft ? this.offsetLeft : null,
right: (this.position == "top" || this.position == "bottom") && this.offsetRight ? this.offsetRight : null,
top: (this.position == "left" || this.position == "right") && this.offsetTop ? this.offsetTop : null,
bottom: (this.position == "left" || this.position == "right") && this.offsetBottom ? this.offsetBottom : null,
} : {};
this._$handleStyle = {
width: this._calcDrawerWidth(),
height: this._calcDrawerHeight(),
...styleTemp
}
}
/**
* 抽屉(div.jigsaw-drawer)宽高的计算方法:
*
* 在浮动模式下:
* 有width和height属性值时,按照width和height的值;
* 否则position为top或bottom时,宽度为100%,跟随容器的宽度,高度为auto,按内容撑开;
* position为left或right时,宽度为auto,按内容撑开,高度为100%,跟随容器的宽度;
*
* 在文档流模式下:
* 当有width和height属性值时,因为width和height是设置在host上的,所以采用计算值calc(100% - 14px)
* 当没有width和height属性值时,则宽高设置为auto,按照内容撑开
*/
private _calcWidth(): string | undefined {
if(this.width && this.width != "auto") {
return this.width;
}
if(this.width == "auto") {
if(this.offsetLeft) {
return `calc(100% - ${this.offsetLeft})`;
}else if(this.offsetRight) {
return `calc(100% - ${this.offsetRight})`;
}
}
return undefined;
}
private _calcDrawerWidth() {
let width = this._calcWidth();
if (this.floating) {
return width ? width : (this.position == 'left' || this.position == 'right' ? 'auto' : '100%');
} else {
return width ? (this.position == 'left' || this.position == 'right' ? 'calc(100% - 14px)' : '100%') : 'auto';
}
}
private _calcHeight(): string | undefined {
if(this.height && this.height != "auto") {
return this.height;
}
if(this.height == "auto") {
if(this.offsetTop) {
return `calc(100% - ${this.offsetTop})`;
}else if(this.offsetBottom) {
return `calc(100% - ${this.offsetBottom})`;
}
}
return undefined;
}
private _calcDrawerHeight() {
let height = this._calcHeight();
if (this.floating) {
return height ? height : (this.position == 'top' || this.position == 'bottom' ? 'auto' : '100%');
} else {
return height ? (this.position == 'top' || this.position == 'bottom' ? 'calc(100% - 14px)' : '100%') : 'auto';
}
}
/**
* @internal
*/
public _$handleClass = {};
/**
* @internal
*/
public _$onAnimation: boolean;
private _setClass() {
this._$handleClass = {
'jigsaw-drawer-left': this.position == 'left',
'jigsaw-drawer-top': this.position == 'top',
'jigsaw-drawer-right': this.position == 'right',
'jigsaw-drawer-bottom': this.position == 'bottom',
'jigsaw-drawer-left-center': this.floating && this.position == 'left' && !this.offsetTop && !this.offsetBottom,
'jigsaw-drawer-right-center': this.floating && this.position == 'right' && !this.offsetTop && !this.offsetBottom,
'jigsaw-drawer-top-center': this.floating && this.position == 'top' && !this.offsetLeft && !this.offsetRight,
'jigsaw-drawer-bottom-center': this.floating && this.position == 'bottom' && !this.offsetLeft && !this.offsetRight,
}
}
public _$toggleOpen(e) {
e.preventDefault();
e.stopPropagation();
this.open = !this.open;
this.openChange.emit(this.open);
}
private _setContainer() {
if (this.container && this.floating) {
const containerEl = CommonUtils.getParentNodeBySelector(this._elementRef.nativeElement, this.container);
if (containerEl) {
const containerStyle = getComputedStyle(containerEl);
if (!containerStyle.position || containerStyle.position == 'static') {
containerEl.style.position = 'relative';
}
if ((this.position == 'left' || this.position == 'right') && containerStyle.overflowX != 'hidden') {
containerEl.style.overflowX = 'hidden';
}
if ((this.position == 'top' || this.position == 'bottom') && containerStyle.overflowY != 'hidden') {
containerEl.style.overflowY = 'hidden';
}
} else {
console.error('Can not find drawer container.');
}
}
}
private _update() {
if (!this.initialized) return;
this._setStyle();
this._setClass();
this.callLater(() => {
// 等待抽屉的尺寸渲染完毕
this._setHostSize();
})
}
ngOnInit() {
super.ngOnInit();
this._update();
this.callLater(() => {
// 等待视图初始化完成,获取computedStyle
this._setContainer();
// 异步添加动画,为了初始化时没有拉伸的动作
this._$onAnimation = true;
});
}
}
export class JigsawDrawerModule {
}