UNPKG

@medyll/idae-be

Version:

A modern, lightweight, and extensible DOM manipulation library built with TypeScript. Designed for precise element targeting and manipulation using a callback-based approach. Features include advanced DOM traversal, event handling, style management, attri

191 lines (190 loc) 8.34 kB
import { Be, be } from '../be.js'; import { BeUtils } from '../utils.js'; var positionMethods; (function (positionMethods) { positionMethods["clonePosition"] = "clonePosition"; positionMethods["overlapPosition"] = "overlapPosition"; positionMethods["snapTo"] = "snapTo"; })(positionMethods || (positionMethods = {})); export class PositionHandler { beElement; static methods = Object.values(positionMethods); constructor(beElement) { this.beElement = beElement; } methods = PositionHandler.methods; handle(actions) { Object.entries(actions).forEach(([method, props]) => { switch (method) { case 'clonePosition': this.clonePosition(props.source, props.options, props.callback); break; case 'overlapPosition': this.overlapPosition(props.source, props.options, props.callback); break; case 'snapTo': this.snapTo(props.target, props.options, props.callback); break; } }); return this.beElement; } /** * Clones the position of a source element to this element. * @param source - The element or selector of the element whose position is to be cloned. * @param options - Additional options for positioning. * @param options.offsetX - Horizontal offset from the source position. * @param options.offsetY - Vertical offset from the source position. * @param options.useTransform - Whether to use CSS transform for positioning. * @param callback - Optional callback function to execute after cloning the position. * @returns The Be instance for method chaining. * @example * // HTML: <div id="source"></div><div id="target"></div> * const beInstance = be('#target'); * beInstance.clonePosition('#source', { offsetX: 10, offsetY: 20 }); */ clonePosition(source, options = {}, callback) { if (this.beElement.isWhat !== 'element') return this.beElement; const sourceEl = typeof source === 'string' ? document.querySelector(source) : source; if (!sourceEl) return this.beElement; const sourceRect = sourceEl.getBoundingClientRect(); const targetRect = this.beElement.inputNode.getBoundingClientRect(); const { offsetX = 0, offsetY = 0, useTransform = false } = options; this.beElement.eachNode((el) => { if (useTransform) { const x = sourceRect.left - targetRect.left + offsetX; const y = sourceRect.top - targetRect.top + offsetY; el.style.transform = `translate(${x}px, ${y}px)`; } else { el.style.left = `${sourceRect.left + offsetX}px`; el.style.top = `${sourceRect.top + offsetY}px`; } // callback?.({ fragment: undefined, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Positions this element to overlap a target element. * @param targetElement - The element or selector of the element to overlap. * @param options - Additional options for positioning. * @param options.alignment - The alignment of this element relative to the target. * @param options.offset - The distance to offset from the target element. * @param options.useTransform - Whether to use CSS transform for positioning. * @param callback - Optional callback function to execute after positioning. * @returns The Be instance for method chaining. * @example * // HTML: <div id="source"></div><div id="target"></div> * const beInstance = be('#target'); * beInstance.overlapPosition('#source', { alignment: 'center', offset: 10 }); */ overlapPosition(targetElement, options = {}, callback) { if (this.beElement.isWhat !== 'element') return this.beElement; const targetEl = typeof targetElement === 'string' ? document.querySelector(targetElement) : targetElement; if (!targetEl) return this.beElement; const { alignment = 'center', offset = 0, useTransform = false } = options; const targetRect = targetEl.getBoundingClientRect(); const selfRect = this.beElement.inputNode.getBoundingClientRect(); let x = 0, y = 0; switch (alignment) { case 'center': x = targetRect.left + (targetRect.width - selfRect.width) / 2; y = targetRect.top + (targetRect.height - selfRect.height) / 2; break; case 'top': x = targetRect.left + (targetRect.width - selfRect.width) / 2; y = targetRect.top - selfRect.height - offset; break; case 'bottom': x = targetRect.left + (targetRect.width - selfRect.width) / 2; y = targetRect.bottom + offset; break; case 'left': x = targetRect.left - selfRect.width - offset; y = targetRect.top + (targetRect.height - selfRect.height) / 2; break; case 'right': x = targetRect.right + offset; y = targetRect.top + (targetRect.height - selfRect.height) / 2; break; } this.beElement.eachNode((el) => { if (useTransform) { el.style.transform = `translate(${x}px, ${y}px)`; } else { el.style.left = `${x}px`; el.style.top = `${y}px`; } callback?.({ fragment: undefined, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Snaps the element to a target element with specified anchor points. * @param targetElement - The element or selector of the element to snap to. * @param options - Snapping options. * @param options.sourceAnchor - The anchor point on the source element. * @param options.targetAnchor - The anchor point on the target element. * @param options.offset - Optional offset from the target anchor point. * @param callback - Optional callback function to execute after snapping. * @returns The Be instance for method chaining. * @example * // HTML: <div id="source"></div><div id="target"></div> * const beInstance = be('#target'); * beInstance.snapTo('#source', { * sourceAnchor: 'center', * targetAnchor: 'top left', * offset: { x: 10, y: 20 } * }); */ snapTo(targetElement, options, callback) { if (this.beElement.isWhat !== 'element') return this.beElement; const targetEl = typeof targetElement === 'string' ? document.querySelector(targetElement) : targetElement; if (!targetEl) return this.beElement; const sourceRect = this.beElement.inputNode.getBoundingClientRect(); const targetRect = targetEl.getBoundingClientRect(); const { sourceAnchor, targetAnchor, offset = { x: 0, y: 0 } } = options; const [sourceX, sourceY] = BeUtils.calculateAnchorPoint(sourceRect, sourceAnchor); const [targetX, targetY] = BeUtils.calculateAnchorPoint(targetRect, targetAnchor); // Calculate final position const x = targetX - sourceX + offset.x; const y = targetY - sourceY + offset.y; // Apply position this.beElement.eachNode((el) => { const computedStyle = window.getComputedStyle(el); const position = computedStyle.position; if (position === 'static') { el.style.position = 'relative'; } el.style.left = `${x}px`; el.style.top = `${y}px`; callback?.({ fragment: undefined, be: be(el), root: this.beElement }); }); return this.beElement; } valueOf() { if (this.beElement.isWhat !== 'element') return null; return this.beElement.inputNode.getBoundingClientRect(); } }