UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

191 lines (185 loc) 6.42 kB
// SPDX-License-Identifier: Apache-2.0 import GirafeHTMLElement from './GirafeHTMLElement.js'; /* Minimal template for a draggable object : It must have 2 divs : - One for the whole panel (id="panel"). Attribute dock is mandatory and can have the values "left" or "right" - One for the gutter (id="gutter"). This is where the panel can be resized. Example: <div id="panel" dock="left"> <div id="gutter"></div> <div id="close"></div> </div> Then in order to make an component resizable, the base method makeResizable() must be called after rendering the template. That's it. */ class GirafeResizableElement extends GirafeHTMLElement { gutter; closeButton; dock; prevX = 0; prevY = 0; toggleSize = 0; lastWidth = 0; minWidth; maxWidth; lastHeight = 0; minHeight; maxHeight; constructor(component, docking) { super(component); const dock = docking ?? this.getAttribute('dock'); if (!dock) { this.dock = 'right'; } else if (dock === 'left' || dock === 'right' || dock === 'bottom') { this.dock = dock; } else { throw new Error(`Invalid value for the attribute dock: ${dock}. Should be one of [left, right, bottom]`); } } render() { this.restoreLastDimensions(); super.render(); this.makeResizable(); } makeResizable() { this.gutter = this.shadow.querySelector('#gutter'); this.gutter.onmousedown = (e) => this.mousedown(e); this.gutter.ondblclick = () => this.togglePanelInternal(); this.closeButton = this.shadow.getElementById('close') || undefined; if (this.closeButton) { this.closeButton.onclick = () => this.closePanel(); } this.toggleSize = this.dock === 'bottom' ? this.gutter.getBoundingClientRect().height : this.gutter.getBoundingClientRect().width; this.initSizeLimits(); } initSizeLimits() { // If there is a configured minWidth or maxWidth, we have to take care of it const css = getComputedStyle(this); const minWidth = Number.parseFloat(css.minWidth); this.minWidth = isNaN(minWidth) ? undefined : minWidth; const maxWidth = Number.parseFloat(css.maxWidth); this.maxWidth = isNaN(maxWidth) ? undefined : maxWidth; const minHeight = Number.parseFloat(css.minHeight); this.minHeight = isNaN(minHeight) ? undefined : minHeight; const maxHeight = Number.parseFloat(css.maxHeight); this.maxHeight = isNaN(maxHeight) ? undefined : maxHeight; } mousedown(e) { e.preventDefault(); this.dispatchEvent(new CustomEvent('resize-start')); document.onmousemove = (e) => this.mousemove(e); document.onmouseup = () => this.mouseup(); this.prevX = e.x; this.prevY = e.y; this.lastWidth = Number.parseFloat(getComputedStyle(this).width); this.lastHeight = Number.parseFloat(getComputedStyle(this).height); } mousemove(e) { e.preventDefault(); const newX = this.prevX - e.x; const newY = this.prevY - e.y; if (this.dock === 'left') { this.resizePanelLeft(newX); } else if (this.dock === 'right') { this.resizePanelRight(newX); } else if (this.dock === 'bottom') { this.resizePanelBottom(newY); } } mouseup() { // stop moving when mouse button is released document.onmouseup = null; document.onmousemove = null; this.lastWidth = Number.parseFloat(getComputedStyle(this).width); this.lastHeight = Number.parseFloat(getComputedStyle(this).height); this.dispatchEvent(new CustomEvent('resize-end')); } clean() { this.style.width = ''; this.style.height = ''; } restoreLastDimensions() { if (this.lastWidth) { this.style.width = this.lastWidth + 'px'; } if (this.lastHeight) { this.style.height = this.lastHeight + 'px'; } } togglePanelInternal() { if (this.dock === 'left' || this.dock === 'right') { this.togglePanelVertically(); } else { this.togglePanelHorizontally(); } } togglePanelVertically() { const width = this.getBoundingClientRect().width; if (width <= this.toggleSize) { // Panel is already hidden. // => We reset it to the last width this.style.width = this.lastWidth + 'px'; this.style.minWidth = this.minWidth + 'px'; } else { // Hide the panel this.lastWidth = width; this.style.width = this.toggleSize + 'px'; this.style.minWidth = '0'; } } togglePanelHorizontally() { const height = this.getBoundingClientRect().height; if (height <= this.toggleSize) { // Panel is already hidden. // => We reset it to the last height this.style.height = this.lastHeight + 'px'; this.style.minWidth = this.minHeight + 'px'; } else { // Hide the panel this.lastHeight = height; this.style.height = this.toggleSize + 'px'; this.style.minHeight = '0'; } } getLimitedWidth(width) { if (this.minWidth && width < this.minWidth) { return this.minWidth; } if (this.maxWidth && width > this.maxWidth) { return this.maxWidth; } return width; } getLimitedHeight(height) { if (this.minHeight && height < this.minHeight) { return this.minHeight; } if (this.maxHeight && height > this.maxHeight) { return this.maxHeight; } return height; } resizePanelLeft(newX) { const newWidth = this.getLimitedWidth(this.lastWidth - newX); this.style.width = newWidth + 'px'; } resizePanelRight(newX) { const newWidth = this.getLimitedWidth(this.lastWidth + newX); this.style.width = newWidth + 'px'; } resizePanelBottom(newY) { const newHeight = this.getLimitedHeight(this.lastHeight + newY); this.style.height = newHeight + 'px'; } } export default GirafeResizableElement;