import PlainDraggable from 'plain-draggable' import { onRemove, animateRectTransition } from './utils' /** * Basic class for layout */ export class Layout { /** * Creates a new Layout instance * * @param {Object} options - The options for the layout * @param {string} options.name - The name of the layout * @param {Function} [options.enterHandler] - Handler called when entering the layout * @param {Function} [options.leaveHandler] - Handler called when leaving the layout * @throws {Error} If the layout name is not provided */ constructor (options = {}) { if (!options.name) throw Error('Layout name is not given') this.name = options.name this.enterHandler = options.enterHandler this.leaveHandler = options.leaveHandler } /** * Returns the name of the layout * * @returns {string} The name of the layout */ valueOf = () => this.name } /** * Side-By-Side Layout, HTML content and Showcase show on left/right side * * @extends {Layout} */ export class SideBySide extends Layout { /** * @type {} */ name = 'side-by-side' /** * Handler called when entering the Side-By-Side layout * * @param {Object} options - The options object * @param {HTMLElement} options.container - The main container element * @param {HTMLElement} options.htmlHolder - The HTML content holder * @param {HTMLElement} options.showcase - The showcase element */ enterHandler = ({ container, htmlHolder, showcase }) => { const bar = document.createElement('div') bar.className = 'bar' bar.innerHTML = '
' const handle = bar.querySelector('.bar-handle') container.appendChild(bar) // Resize views by value const resizeByLeft = left => { htmlHolder.style.width = left + 'px' showcase.style.width = parseFloat(window.getComputedStyle(container).width) - left + 'px' } const draggable = new PlainDraggable(bar, { handle, containment: { left: '25%', top: 0, right: '75%', height: 0 }, }) draggable.draggableCursor = 'grab' draggable.onDrag = pos => { handle.style.transform = 'unset' resizeByLeft(pos.left) } draggable.onDragEnd = _ => { handle.style.cssText = '' } onRemove(bar, () => draggable.remove()) } /** * Handler called when leaving the Side-By-Side layout * * @param {Object} options - The options object * @param {HTMLElement} options.container - The main container element */ leaveHandler = ({ container }) => { container.querySelector('.bar')?.remove() } } /** * Overlay Layout, Showcase occupies viewport, and HTML content becomes draggable blocks * * @extends {Layout} */ export class Overlay extends Layout { /** * @type {} */ name = 'overlay' /** * saveLeftTopAsData. * * @param {} element */ saveLeftTopAsData = element => { const { left, top } = element.getBoundingClientRect() element.dataset.left = left element.dataset.top = top } /** * addDraggable. * * @param {} element */ addDraggable = element => { // Make sure current element always on top const siblings = Array.from( element.parentElement?.querySelectorAll(':scope > *') ?? [], ) let popTimer = null element.onmouseover = () => { popTimer = setTimeout(() => { siblings.forEach(e => e.style.removeProperty('z-index')) element.style.zIndex = '9001' }, 200) } element.onmouseout = () => { clearTimeout(popTimer) } // Add draggable part const draggablePart = document.createElement('div') element.appendChild(draggablePart) draggablePart.className = 'draggable-part' draggablePart.innerHTML = '