/** @module react-aiot/components/ResizeButton */
import React from 'react';
import { touchable, resizeOpposite } from '../util';
/**
* Resize button with resizer (three vertical dots) and collapse button.
*
* @param {object} props Properties
* @param {string} props.containerId The container id which should be resized and contains the resize button
* @param {DOMElement} [props.opposite] The opposite which gets also resized (width: calc() method)
* @param {int} [props.oppositeOffset] The offset reduced width in px
* @param {int} [props.restoreWidth] The restored width in px (if you save in localStorage for example)
* @param {int} props.initialWidth If restoreWidth is not setted this width in px is used
* @param {int} [props.minWidth] The minimum width in px
* @param {int} [props.maxWidth] The maximum width in px
* @param {module:react-aiot/components/ResizeButton#onResizeFinished} [props.onResizeFinished]
* @extensd React.Component
*/
export default class ResizeButton extends React.Component {
static stateKeys = 'defaultRestoreWidth,restoreWidth'.split(',');
currentlyResizing = false;
constructor(props) {
super(props);
const { initialWidth, minWidth, restoreWidth } = props,
defaultRestoreWidth = typeof initialWidth === 'number' ? initialWidth : minWidth;
this.state = {
defaultRestoreWidth,
restoreWidth: restoreWidth || defaultRestoreWidth
};
}
/**
* Avoid rerender of the resizebutton during resizing.
*/
shouldComponentUpdate(nextProps, nextState) {
const changedState = ResizeButton.stateKeys.filter(k => this.state[k] !== nextState[k]);
if (changedState.length === 1 && changedState[0] === 'restoreWidth') {
return false;
}
return true;
}
componentDidMount() {
// Add handler to the resize button
this._getContainer('.aiot-split-resizer').addEventListener(touchable.down, this.handleMouseDown);
document.addEventListener(touchable.up, this.handleMouseUp);
// Initialize resize width
const defaultRestoreWidth = this.state.defaultRestoreWidth;
this.handleResize(defaultRestoreWidth);
this.props.onResizeFinished && this.props.onResizeFinished(defaultRestoreWidth);
}
handleDoubleClick = () => {
const width = this._getContainerWidth() > 0 ? 0 : this.state.restoreWidth;
this.handleResize(width);
this.props.onResizeFinished && this.props.onResizeFinished(width);
}
handleMouseDown = ev => {
ev.preventDefault();
document.addEventListener(touchable.move, this.handleResize);
this.currentlyResizing = true;
}
handleMouseUp = ev => {
document.removeEventListener(touchable.move, this.handleResize);
/**
* This function is called when the container is resized.
*
* @callback module:react-aiot/components/ResizeButton#onResizeFinished
* @param {int} width The width in px
*/
this.currentlyResizing && this.props.onResizeFinished
&& this.props.onResizeFinished(this._getContainerWidth());
this.currentlyResizing = false;
}
handleOpposite = width => {
return resizeOpposite(this._container.id, this.props.opposite.id, width);
}
handleResize = (ev, force) => {
const { minWidth, maxWidth } = this.props,
isEvent = !!(ev && ev.pageX);
let x = (isEvent
? ev.pageX - (this._container.getBoundingClientRect().left + document.body.scrollLeft) - 15
: ev), // Offset
move = x >= minWidth && x <= maxWidth;
// Prevent default if event
isEvent && ev.preventDefault();
// Allow collapse
x < minWidth - 50 && (move = x = 1);
const collapse = move === 1, xOpposite = x + this.props.oppositeOffset;
window.requestAnimationFrame(() => {
if ((move || force) && this.handleOpposite(collapse ? x : xOpposite) !== false) {
this._container.style.width = xOpposite + 'px';
!collapse && this.setState({ restoreWidth: x });
/**
* This function is called while container gets resized.
*
* @callback module:react-aiot/components/ResizeButton#onResize
* @param {int} width The width in px
* @param {boolean} collapsed If true the sidebar is collapsed
*/
this.props.onResize && this.props.onResize(x, collapse);
}
});
}
render() {
return <span className="aiot-split">
<div className="aiot-split-resizer" />
<div className="aiot-split-collapse" onClick={ this.handleDoubleClick } />
</span>;
}
_getContainer(find, singleFind = true) {
const elem = document.getElementById(this.props.containerId),
findObj = find ? elem && elem.querySelectorAll(find) : elem;
this._container = elem;
if (find && singleFind) {
return findObj && findObj[0];
}
return findObj;
}
_getContainerWidth() {
const computed = window.getComputedStyle(this._container),
width = parseInt(computed.width, 10);
return width - parseInt(computed.borderLeftWidth, 10) - parseInt(computed.borderRightWidth, 10);
}
}