import { type EventListenerFunc, attr, godown, htmlSlot, styles } from "@godown/element"; import { type TemplateResult, css, html } from "lit"; import { property } from "lit/decorators.js"; import { GlobalStyle } from "../../internal/global-style.js"; const protoName = "dragbox"; /** * {@linkcode Dragbox} moves with the mouse and does not exceed the boundary of offsetParent. * * @slot - Dragbox content. * @category layout */ @godown(protoName) @styles(css` :host { position: absolute; display: block; } :host(:active) { -webkit-user-select: none; user-select: none; } `) class Dragbox extends GlobalStyle { /** * Offset parent or document.body. */ get _offsetParent(): Element { return this.offsetParent ?? document.body; } private __drag = false; private __t: number; private __l: number; private __x: number; private __y: number; /** * Position x. */ @property() x: string; /** * Position y. */ @property() y: string; protected render(): TemplateResult<1> { return html`
${htmlSlot()}
`; } protected firstUpdated(): void { this.reset(); } protected _handleDragStart(e: MouseEvent): void { this.__x = e.x; this.__y = e.y; const parentRect = this._offsetParent.getBoundingClientRect(); const rect = this.getBoundingClientRect(); this.__t = rect.top - parentRect.top; this.__l = rect.left - parentRect.left; this.__drag = true; this.__handleMouseMove = this.events.add(document, "mousemove", this._handleDrag.bind(this)); this.__handleMouseLeave = this.events.add(document, "mouseleave", this._handleDragEnd.bind(this)); this.__handleMouseUp = this.events.add(document, "mouseup", this._handleDragEnd.bind(this)); } private __handleMouseMove: EventListenerFunc; private __handleMouseLeave: EventListenerFunc; private __handleMouseUp: EventListenerFunc; protected _handleDragEnd(): void { this.__drag = false; this.events.remove(document, "mousemove", this.__handleMouseMove); this.events.remove(document, "mouseleave", this.__handleMouseLeave); this.events.remove(document, "mouseup", this.__handleMouseUp); } protected _handleDrag(e: MouseEvent): void { if (!this.__drag) { return; } const { __x, __y, __l, __t, style } = this; const { height: parentHeight, width: parentWidth } = this._offsetParent.getBoundingClientRect(); const { width, height } = this.getBoundingClientRect(); const l = e.x - (__x - __l); const t = e.y - (__y - __t); if (l < 0) { style.left = "0"; } else if (l < parentWidth - width) { style.left = `${l}px`; } else { style.left = `${parentWidth - width}px`; } if (t < 0) { style.top = "0"; } else if (t < parentHeight - height) { style.top = `${t}px`; } else { style.top = `${parentHeight - height}px`; } } reset(): void { const { x, y, style, offsetWidth, offsetHeight, offsetLeft, offsetTop } = this; const { height: parentHeight, width: parentWidth } = this._offsetParent.getBoundingClientRect(); style.left = x || "0"; style.top = y || "0"; if (offsetLeft > parentWidth - offsetWidth) { style.left = `${parentWidth - offsetWidth}px`; } if (offsetTop > parentHeight - offsetHeight) { style.top = `${parentHeight - offsetHeight}px`; } } } export default Dragbox; export { Dragbox };