@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
191 lines (185 loc) • 6.42 kB
JavaScript
// 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;