ant-design-blazor/components/core/JsInterop/modules/dragHelper.ts
zxyao ef4701b6ed refactor(module: modal): refactor the code of Modal, Confirm (#914)
* refactor(module: modal): refactor the code of Modal, Confirm and Dialog

refactor details:
- Add DialogWrapper compontent to control the logic of dialog removal from DOM
- Add the appropriate folders for Dialog, Modal, and Confirm
- Remove methods with the same logic but different names in ConfirmService
- Split Title into Title (string) and Titletemplate (renderfragment)
- Add comments to code
- Rename ConfirmDialog to Confirm
- Specification of method name in ConfirmService
- Adjust the time of throttle function for draggabe modal
- Extract the common part of ModalOptions, ConfirmOptions and DialogOptions as DialogOptionsBase

* refactor: move DefaultCloseIcon etc. static members to DialogOptionsBase

* docs(module: modal): update docs

* fix: dialog incorrect waiting

* fix: pickup missing pr/7
2020-12-26 21:39:04 +08:00

206 lines
6.2 KiB
TypeScript

const throttle = (fn, threshold = 160) => {
let timeout;
var start = +new Date;
return function (...args) {
let context = this, curTime = +new Date() - 0;
//总是干掉事件回调
window.clearTimeout(timeout);
if (curTime - start >= threshold) {
//只执行一部分方法,这些方法是在某个时间段内执行一次
fn.apply(context, args);
start = curTime;
}
else {
//让方法在脱离事件后也能执行一次
timeout = window.setTimeout(() => {
//@ts-ignore
fn.apply(this, args);
}, threshold);
}
};
}
const eventMap = new Map<HTMLElement, Dragger>();
const defaultOptions = {
inViewport: true
}
class Dragger {
private _triggler: HTMLElement = null;
private _container: HTMLElement = null;
private _options: any = null;
private _state: any = null;
private _isFirst: boolean = true;
private _style: string = null;
constructor(triggler: HTMLElement, container: HTMLElement, dragInViewport: boolean) {
this._triggler = triggler;
this._container = container;
this._options = Object.assign({}, defaultOptions, {
inViewport: dragInViewport
});
this._state = {
isInDrag: false,
mX: 0, // mouse x
mY: 0, // mouse y
domStartX: 0, // on mousedown, the mouse x
domStartY: 0, // on mousedown, the mouse y
}
}
getContainerPos() {
const rect = this._container.getBoundingClientRect();
return {
left: rect.left,
top: rect.top
}
}
onMousedown = (e) => {
const state = this._state;
state.isInDrag = true;
state.mX = e.clientX;
state.mY = e.clientY;
this._container.style.position = "absolute";
const { left, top } = this.getContainerPos();
if (this._isFirst) {
state.domMaxY = document.documentElement.clientHeight
- this._container.offsetHeight - 1;
state.domMaxX = document.documentElement.clientWidth
- this._container.offsetWidth - 1;
this._container.style.left = left + 'px';
this._container.style.top = top + 'px';
if (!this._style) {
this._style = this._container.getAttribute("style");
}
this._isFirst = false;
}
state.domStartX = left;
state.domStartY = top;
}
onMouseup = (e) => {
const state = this._state;
state.isInDrag = false;
const { left, top } = this.getContainerPos();
state.domStartX = left;
state.domStartY = top;
}
onMousemove = throttle((e) => {
const state = this._state;
if (state.isInDrag) {
var nowX = e.clientX,
nowY = e.clientY,
disX = nowX - state.mX,
disY = nowY - state.mY;
var newDomX = state.domStartX + disX;
var newDomY = state.domStartY + disY;
if (this._options.inViewport) {
if (newDomX < 0) {
newDomX = 0;
}
else if (newDomX > state.domMaxX) {
newDomX = state.domMaxX;
}
if (newDomY < 0) {
newDomY = 0;
}
else if (newDomY > state.domMaxY) {
newDomY = state.domMaxY;
}
}
this._container.style.position = "absolute";
this._container.style.margin = "0";
this._container.style.paddingBottom = "0";
this._container.style.left = newDomX + "px";
this._container.style.top = newDomY + "px";
}
}, 10).bind(this);
onResize = throttle((e) => {
const state = this._state;
state.domMaxY = document.documentElement.clientHeight
- this._container.offsetHeight - 1;
state.domMaxX = document.documentElement.clientWidth
- this._container.offsetWidth - 1;
state.domStartY = parseInt(this._container.style.top);
state.domStartX = parseInt(this._container.style.left);
if (state.domStartY > state.domMaxY) {
if (state.domMaxY > 0) {
this._container.style.top = state.domMaxY + "px";
}
}
if (state.domStartX > state.domMaxX) {
this._container.style.left = state.domMaxX + "px";
}
}, 10).bind(this);
bindDrag() {
const triggler = this._triggler;
const options = this._options;
triggler.addEventListener("mousedown", this.onMousedown, false);
window.addEventListener("mouseup", this.onMouseup, false);
document.addEventListener("mousemove", this.onMousemove);
if (options.inViewport) {
window.addEventListener("resize", this.onResize, false);
}
}
unbindDrag() {
const triggler = this._triggler;
triggler.removeEventListener("mousedown", this.onMousedown, false);
window.removeEventListener("mouseup", this.onMouseup, false);
document.removeEventListener("mousemove", this.onMousemove);
if (this._options.inViewport) {
window.removeEventListener("resize", this.onResize, false);
}
}
resetContainerStyle() {
if (this._style !== null) {
this._isFirst = true;
this._container.setAttribute("style", this._style);
}
}
}
function enableDraggable(triggler: HTMLElement, container: HTMLElement, dragInViewport: boolean = true) {
let dragger = eventMap.get(triggler);
if (!dragger) {
dragger = new Dragger(triggler, container, dragInViewport);
eventMap.set(triggler, dragger);
}
dragger.bindDrag();
}
function disableDraggable(triggler: HTMLElement) {
const dragger = eventMap.get(triggler);
if (dragger) {
dragger.unbindDrag();
}
}
function resetModalPosition(triggler: HTMLElement) {
const dragger = eventMap.get(triggler);
if (dragger) {
dragger.resetContainerStyle();
}
}
export { enableDraggable, disableDraggable, resetModalPosition };