/*
* Copyright (C) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BaseElement, element } from '../BaseElement';
let width = '';
let css = `
`
const initHtmlStyle = (wid: string) => {
width = wid;
return css;
}
@element('lit-modal')
export class LitModal extends BaseElement {
private headerTitleElement: HTMLElement | null | undefined;
private headerElement: HTMLElement | null | undefined;
private closeElement: HTMLElement | null | undefined;
private cancelElement: HTMLElement | null | undefined;
private okElement: HTMLElement | null | undefined;
private modalElement: HTMLElement | null | undefined;
private resizing: boolean = false;
private down: boolean = false;
private onmouseleaveMoveFunc: any;
private onmouseupFunc: any;
private onmousedownMoveFunc: any;
private onmousedownFunc: any;
private onmousemoveMoveFunc: any;
private onmousemoveFunc: any;
private onmouseupMoveFunc: any;
static get observedAttributes() {
return [
'title', //标题
'line', //body的线条
'visible', //是否显示modal窗口
'ok-text', //确定文本
'cancel-text', //取消文本
'moveable', //设置窗口是否可以鼠标拖动
'resizeable', //窗口可改变大小
'width', //modal宽度
];
}
get okText() {
return this.getAttribute('ok-text') || '确定';
}
set okText(value) {
this.setAttribute('ok-text', value);
}
get cancelText() {
return this.getAttribute('cancel-text') || '取消';
}
set cancelText(value) {
this.setAttribute('cancel-text', value);
}
get title() {
return this.getAttribute('title') || '';
}
set title(value) {
this.setAttribute('title', value);
}
get visible() {
return this.hasAttribute('visible');
}
set visible(value) {
if (value) {
this.setAttribute('visible', '');
} else {
this.removeAttribute('visible');
}
}
get width() {
return this.getAttribute('width') || '500px';
}
set width(value) {
this.setAttribute('width', value);
}
get resizeable() {
return this.hasAttribute('resizeable');
}
set resizeable(value) {
if (value) {
this.setAttribute('resizeable', '');
} else {
this.removeAttribute('resizeable');
}
}
get moveable() {
return this.hasAttribute('moveable');
}
set moveable(value) {
if (value) {
this.setAttribute('moveable', '');
} else {
this.removeAttribute('moveable');
}
}
set onOk(fn: any) {
this.addEventListener('onOk', fn);
}
set onCancel(fn: any) {
this.addEventListener('onCancel', fn);
}
initHtml(): string {
return `
${initHtmlStyle(this.width)}
`;
}
//当 custom element首次被插入文档DOM时,被调用。
initElements(): void {
this.headerTitleElement = this.shadowRoot!.querySelector('#modal-title');
this.headerTitleElement!.textContent = this.title;
this.headerElement = this.shadowRoot!.querySelector('.header');
this.closeElement = this.shadowRoot!.querySelector('.close-icon');
this.cancelElement = this.shadowRoot!.querySelector('#cancel');
this.okElement = this.shadowRoot!.querySelector('#ok');
this.closeElement!.onclick = (ev) => (this.visible = false);
this.modalElement = this.shadowRoot!.querySelector('.modal');
this.shadowRoot!.querySelector('.modal')!.onclick = (e) => {
e.stopPropagation();
};
this.setClick();
if (this.moveable || this.resizeable) {
if (this.resizeable) {
let resizeWidth = 8;
this.resizing = false;
let srcResizeClientX = 0,
srcResizeClientY = 0,
srcResizeRect,
srcResizeHeight = 0,
srcResizeWidth = 0,
srcResizeRight = 0,
srcResizeLeft = 0,
srcResizeTop = 0;
let direction: string;
this.onmousemoveFunc = (e: any) => {
e.stopPropagation();
srcResizeRect = this.modalElement!.getBoundingClientRect();
direction = this.onmousemoveFuncRule(direction,e,srcResizeRect,resizeWidth);
this.resizingFunc(direction,e,srcResizeClientX,srcResizeClientY,srcResizeHeight,srcResizeWidth,srcResizeLeft,srcResizeTop);
};
this.setOnmousedownFunc(resizeWidth);
this.onmouseupFunc = (e: any) => {
this.resizing = false;
};
}
this.buildFunc();
this.onmouseleave = this.onmouseup = (e) => {
if (this.onmouseleaveMoveFunc) this.onmouseleaveMoveFunc(e);
if (this.onmouseupFunc) this.onmouseupFunc(e);
document.body.style.userSelect = '';
};
}
}
setClick():void{
this.onclick = (ev) => {
ev.stopPropagation();
if (!this.resizeable) {
this.visible = false;
}
};
this.cancelElement!.onclick = (ev) => {
this.dispatchEvent(new CustomEvent('onCancel', ev));
};
this.okElement!.onclick = (ev) => {
this.dispatchEvent(new CustomEvent('onOk', ev));
};
}
buildFunc():void{
if (this.moveable) {
this.down = false;
let srcClientX = 0;
let srcClientY = 0;
let srcLeft = 0;
let srcTop = 0;
let srcRight = 0;
let srcBottom = 0;
let clientRect;
let rootRect: any;
this.onmousedownMoveFunc = (e: any) => {
if (this.resizing) return;
srcClientX = e.clientX;
srcClientY = e.clientY;
rootRect = this.getBoundingClientRect();
clientRect = this.modalElement!.getBoundingClientRect();
srcLeft = clientRect.left;
srcRight = clientRect.right;
srcTop = clientRect.top;
srcBottom = clientRect.bottom;
if (
e.clientX > srcLeft + 10 &&
e.clientX < srcRight - 10 &&
e.clientY > srcTop + 10 &&
e.clientY < srcTop + this.headerElement!.scrollHeight
) {
this.down = true;
} else {
this.down = false;
}
if (this.down) document.body.style.userSelect = 'none';
this.setOnmousemoveMoveFunc(e, srcClientX, srcClientY, srcLeft, srcTop, srcRight, srcBottom, clientRect, rootRect);
this.onmouseleaveMoveFunc = this.onmouseupMoveFunc = (e: any) => {
this.down = false;
this.headerElement!.style.cursor = '';
};
};
}
this.onmousemove = (e) => {
if (this.onmousemoveFunc) this.onmousemoveFunc(e);
if (this.onmousemoveMoveFunc) this.onmousemoveMoveFunc(e);
};
this.onmousedown = (e) => {
if (this.onmousedownFunc) this.onmousedownFunc(e);
if (this.onmousedownMoveFunc) this.onmousedownMoveFunc(e);
};
}
setOnmousemoveMoveFunc(e:any,srcClientX:number,srcClientY:number,srcLeft:number,srcTop:number,srcRight:number,srcBottom:number,clientRect:any,rootRect:any):void{
this.onmousemoveMoveFunc = (ev: any) => {
if (this.down) {
let offsetY = e.clientY - srcClientY;
let offsetX = e.clientX - srcClientX;
if (e.clientX > srcLeft + 10 && e.clientX < srcRight - 10 && e.clientY > srcTop + 10) {
this.headerElement!.style.cursor = 'move';
clientRect = this.modalElement!.getBoundingClientRect();
//下面 rootRect.height 改成 this.scrollHeight 解决modal 过长会出现滚动条的情况
if (
ev.clientY - srcClientY + srcTop > 0 &&
ev.clientY - srcClientY + srcTop < this.scrollHeight - clientRect.height
) {
this.modalElement!.style.top = ev.clientY - srcClientY + srcTop + 'px';
} else {
if (ev.clientY - srcClientY + srcTop <= 0) {
this.modalElement!.style.top = '0px';
} else {
//下面 rootRect.height 改成 this.scrollHeight 解决modal 过长会出现滚动条的情况
this.modalElement!.style.top = this.scrollHeight - clientRect.height + 'px';
}
}
//ev.clientX-srcClientX 鼠标移动像素
if (
ev.clientX - srcClientX + srcLeft > 0 &&
ev.clientX - srcClientX + srcLeft < rootRect.width - clientRect.width
) {
this.modalElement!.style.left = ev.clientX - srcClientX + srcLeft + clientRect.width / 2 + 'px';
} else {
if (ev.clientX - srcClientX + srcLeft <= 0) {
this.modalElement!.style.left = clientRect.width / 2 + 'px';
} else {
this.modalElement!.style.left = rootRect.width - clientRect.width + clientRect.width / 2 + 'px';
}
}
}
}
};
}
onmousemoveFuncRule(direction:string,e:any,srcResizeRect:any,resizeWidth:number):string{
if (
e.clientX > srcResizeRect.left - resizeWidth &&
e.clientX < srcResizeRect.left + resizeWidth &&
e.clientY > srcResizeRect.top - resizeWidth &&
e.clientY < srcResizeRect.top + resizeWidth
) {
this.style.cursor = 'nwse-resize';
if (!this.resizing) return 'left-top';
} else if (
e.clientX > srcResizeRect.right - resizeWidth &&
e.clientX < srcResizeRect.right + resizeWidth &&
e.clientY > srcResizeRect.top - resizeWidth &&
e.clientY < srcResizeRect.top + resizeWidth
) {
this.style.cursor = 'nesw-resize';
if (!this.resizing) return 'right-top';
} else if (
e.clientX > srcResizeRect.left - resizeWidth &&
e.clientX < srcResizeRect.left + resizeWidth &&
e.clientY > srcResizeRect.bottom - resizeWidth &&
e.clientY < srcResizeRect.bottom + resizeWidth
) {
this.style.cursor = 'nesw-resize';
if (!this.resizing) return 'left-bottom';
} else {
return this.funcRuleIf(direction,e,srcResizeRect,resizeWidth);
}
return ''
}
funcRuleIf(direction:string,e:any,srcResizeRect:any,resizeWidth:number):string{
if (
e.clientX > srcResizeRect.right - resizeWidth &&
e.clientX < srcResizeRect.right + resizeWidth &&
e.clientY > srcResizeRect.bottom - resizeWidth &&
e.clientY < srcResizeRect.bottom + resizeWidth
) {
this.style.cursor = 'nwse-resize';
if (!this.resizing) return 'right-bottom';
} else if (e.clientX > srcResizeRect.left - resizeWidth && e.clientX < srcResizeRect.left + resizeWidth) {
this.style.cursor = 'ew-resize';
if (!this.resizing) return 'left';
} else if (e.clientX < srcResizeRect.right + resizeWidth && e.clientX > srcResizeRect.right - resizeWidth) {
this.style.cursor = 'ew-resize';
if (!this.resizing) return 'right';
} else if (e.clientY > srcResizeRect.top - resizeWidth && e.clientY < srcResizeRect.top + resizeWidth) {
this.style.cursor = 'ns-resize';
if (!this.resizing) return 'top';
} else if (e.clientY < srcResizeRect.bottom + resizeWidth && e.clientY > srcResizeRect.bottom - resizeWidth) {
this.style.cursor = 'ns-resize';
if (!this.resizing) return 'bottom';
} else {
this.style.cursor = '';
if (!this.resizing) return '';
}
return '';
}
resizingFunc(direction:string,e:any,srcResizeClientX:number,srcResizeClientY:number,srcResizeHeight:number,srcResizeWidth:number,srcResizeLeft:number,srcResizeTop:number):void{
if (this.resizing) {
let offsetResizeY = e.clientY - srcResizeClientY;
let offsetResizeX = e.clientX - srcResizeClientX;
if (direction === 'bottom') {
this.modalElement!.style.height = srcResizeHeight + offsetResizeY + 'px';
} else if (direction === 'top') {
this.modalElement!.style.top = srcResizeTop + offsetResizeY + 'px';
this.modalElement!.style.height = srcResizeHeight - offsetResizeY + 'px';
} else if (direction === 'right') {
this.modalElement!.style.left = srcResizeLeft + srcResizeWidth / 2 + offsetResizeX / 2 + 'px';
this.modalElement!.style.width = srcResizeWidth + offsetResizeX + 'px';
} else if (direction === 'left') {
this.modalElement!.style.left = srcResizeLeft + srcResizeWidth / 2 + offsetResizeX / 2 + 'px';
this.modalElement!.style.width = srcResizeWidth - offsetResizeX + 'px';
} else if (direction === 'left-top') {
this.modalElement!.style.left = srcResizeLeft + srcResizeWidth / 2 + offsetResizeX / 2 + 'px';
this.modalElement!.style.width = srcResizeWidth - offsetResizeX + 'px';
this.modalElement!.style.top = srcResizeTop + offsetResizeY + 'px';
this.modalElement!.style.height = srcResizeHeight - offsetResizeY + 'px';
} else if (direction === 'right-top') {
this.modalElement!.style.left = srcResizeLeft + srcResizeWidth / 2 + offsetResizeX / 2 + 'px';
this.modalElement!.style.width = srcResizeWidth + offsetResizeX + 'px';
this.modalElement!.style.top = srcResizeTop + offsetResizeY + 'px';
this.modalElement!.style.height = srcResizeHeight - offsetResizeY + 'px';
} else if (direction === 'left-bottom') {
this.modalElement!.style.left = srcResizeLeft + srcResizeWidth / 2 + offsetResizeX / 2 + 'px';
this.modalElement!.style.width = srcResizeWidth - offsetResizeX + 'px';
this.modalElement!.style.height = srcResizeHeight + offsetResizeY + 'px';
} else if (direction === 'right-bottom') {
this.modalElement!.style.left = srcResizeLeft + srcResizeWidth / 2 + offsetResizeX / 2 + 'px';
this.modalElement!.style.width = srcResizeWidth + offsetResizeX + 'px';
this.modalElement!.style.height = srcResizeHeight + offsetResizeY + 'px';
}
}
}
setOnmousedownFunc(resizeWidth: number): void {
this.onmousedownFunc = (e: any) => {
const srcResizeRect = this.modalElement!.getBoundingClientRect();
const { clientX, clientY } = e;
const { left, right, top, bottom, width, height } = srcResizeRect;
const resizeRange = resizeWidth * 2;
const isWithinRange = (coord: number, target: number, range: number) =>
coord > target - range && coord < target + range;
const isWithinCornerRange = (cornerX: number, cornerY: number) =>
isWithinRange(clientX, cornerX, resizeRange) &&
isWithinRange(clientY, cornerY, resizeRange);
const isWithinTopLeft = isWithinCornerRange(left, top);
const isWithinTopRight = isWithinCornerRange(right, top);
const isWithinBottomLeft = isWithinCornerRange(left, bottom);
const isWithinBottomRight = isWithinCornerRange(right, bottom);
if (
isWithinTopLeft ||
isWithinTopRight ||
isWithinBottomLeft ||
isWithinBottomRight
) {
this.resizing = true;
} else {
this.resizeIf(e, srcResizeRect, resizeWidth);
}
if (this.resizing) {
document.body.style.userSelect = 'none';
}
};
}
resizeIf(e:any,srcResizeRect:any,resizeWidth:number){
if (e.clientX > srcResizeRect.left - resizeWidth && e.clientX < srcResizeRect.left + resizeWidth) {
this.resizing = true;
} else if (e.clientX < srcResizeRect.right + resizeWidth && e.clientX > srcResizeRect.right - resizeWidth) {
this.resizing = true;
} else if (e.clientY > srcResizeRect.top - resizeWidth && e.clientY < srcResizeRect.top + resizeWidth) {
this.resizing = true;
} else if (e.clientY < srcResizeRect.bottom + resizeWidth && e.clientY > srcResizeRect.bottom - resizeWidth) {
this.resizing = true;
} else {
this.resizing = false;
}
}
//当 custom element从文档DOM中删除时,被调用。
disconnectedCallback() {}
//当 custom element被移动到新的文档时,被调用。
adoptedCallback() {}
//当 custom element增加、删除、修改自身属性时,被调用。
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (name === 'visible') {
if (!newValue && this.modalElement) {
this.modalElement.style.top = '100px';
this.modalElement.style.left = '50%';
}
} else if (name === 'title' && this.headerTitleElement) {
this.headerTitleElement.textContent = newValue;
}
}
}
if (!customElements.get('lit-modal')) {
customElements.define('lit-modal', LitModal);
}