TeaWeb/shared/js/ui/react-elements/ContextDivider.tsx

175 lines
6.1 KiB
TypeScript
Raw Normal View History

2020-06-15 16:56:05 +02:00
import * as React from "react";
2020-07-19 18:49:00 +02:00
import {Settings, settings} from "tc-shared/settings";
2020-06-15 16:56:05 +02:00
const cssStyle = require("./ContextDivider.scss");
export interface ContextDividerProperties {
id: string;
direction: "vertical" | "horizontal";
defaultValue: number; /* [0;100] */
separatorClassName?: string;
separatorActiveClassName?: string;
children?: never;
2020-06-15 16:56:05 +02:00
}
export interface ContextDividerState {
active: boolean;
}
export class ContextDivider extends React.Component<ContextDividerProperties, ContextDividerState> {
private readonly refSeparator = React.createRef<HTMLDivElement>();
private readonly listenerMove;
private readonly listenerUp;
private value;
constructor(props) {
super(props);
this.state = {
active: false
};
this.value = this.props.defaultValue;
try {
2021-01-10 16:13:15 +01:00
const config = JSON.parse(settings.getValue(Settings.FN_SEPARATOR_STATE(this.props.id), undefined));
2020-06-15 16:56:05 +02:00
if(typeof config.value !== "number")
throw "Invalid value";
this.value = config.value;
} catch (e) { }
this.listenerMove = (event: MouseEvent | TouchEvent) => {
const separator = this.refSeparator.current;
if(!separator) {
this.setState({ active: false });
return;
}
const previousElement = separator.previousElementSibling as HTMLElement;
const nextElement = separator.nextElementSibling as HTMLElement;
const beforeBounds = previousElement.getBoundingClientRect();
const afterBounds = nextElement.getBoundingClientRect();
let min: number, max: number;
if(this.props.direction === "horizontal") {
min = Math.min(beforeBounds.left, afterBounds.left);
if(min === beforeBounds.left) {
max = afterBounds.left + afterBounds.width;
} else {
max = beforeBounds.left + beforeBounds.width;
}
} else {
min = Math.min(beforeBounds.top, afterBounds.top);
if(min === beforeBounds.top) {
max = afterBounds.top + afterBounds.height;
} else {
max = beforeBounds.top + beforeBounds.height;
}
}
2020-06-15 16:56:05 +02:00
const current = event instanceof MouseEvent ?
(this.props.direction === "horizontal" ? event.pageX : event.pageY) :
(this.props.direction === "horizontal" ? event.touches[event.touches.length - 1].clientX : event.touches[event.touches.length - 1].clientY);
if(current < min) {
this.value = 0;
} else if(current < max) {
const x_offset = current - min;
const x_offset_max = max - min;
this.value = x_offset * 100 / x_offset_max;
} else {
this.value = 100;
}
this.applySeparator(separator.previousElementSibling as HTMLElement, separator.nextElementSibling as HTMLElement);
2020-06-15 16:56:05 +02:00
};
this.listenerUp = () => this.stopMovement();
}
render() {
2020-07-17 23:56:20 +02:00
let separatorClassNames = cssStyle.separator;
2020-06-15 16:56:05 +02:00
if(this.props.direction === "vertical") {
2020-06-15 16:56:05 +02:00
separatorClassNames += " " + cssStyle.vertical;
} else {
2020-06-15 16:56:05 +02:00
separatorClassNames += " " + cssStyle.horizontal;
}
2020-06-15 16:56:05 +02:00
if(this.props.separatorClassName) {
2020-07-17 23:56:20 +02:00
separatorClassNames += " " + this.props.separatorClassName;
}
2020-07-17 23:56:20 +02:00
if(this.state.active && this.props.separatorClassName) {
2020-06-15 16:56:05 +02:00
separatorClassNames += " " + this.props.separatorClassName;
}
2020-06-15 16:56:05 +02:00
return (
<div key={"context-separator"} ref={this.refSeparator} className={separatorClassNames} onMouseDown={e => this.startMovement(e)} onTouchStart={e => this.startMovement(e)} />
);
2020-06-15 16:56:05 +02:00
}
componentDidMount(): void {
const separator = this.refSeparator.current;
if(!separator) return;
this.applySeparator(separator.previousSibling as HTMLElement, separator.nextSibling as HTMLElement);
}
componentWillUnmount(): void {
this.stopMovement();
}
private startMovement(event: React.MouseEvent | React.TouchEvent) {
this.setState({ active: true });
document.addEventListener('mousemove', this.listenerMove);
document.addEventListener('touchmove', this.listenerMove);
document.addEventListener('mouseup', this.listenerUp);
document.addEventListener('touchend', this.listenerUp);
document.addEventListener('touchcancel', this.listenerUp);
document.documentElement.classList.add(cssStyle.documentActiveClass);
this.listenerMove(event.nativeEvent);
}
private stopMovement() {
this.setState({ active: false });
document.removeEventListener('mousemove', this.listenerMove);
document.removeEventListener('touchmove', this.listenerMove);
document.removeEventListener('mouseup', this.listenerUp);
document.removeEventListener('touchend', this.listenerUp);
document.removeEventListener('touchcancel', this.listenerUp);
document.documentElement.classList.remove(cssStyle.documentActiveClass);
2021-01-10 16:13:15 +01:00
settings.setValue(Settings.FN_SEPARATOR_STATE(this.props.id), JSON.stringify({
value: this.value
}));
2020-06-15 16:56:05 +02:00
}
private applySeparator(previousElement: HTMLElement, nextElement: HTMLElement) {
if(!this.refSeparator.current || !previousElement || !nextElement) {
2020-06-15 16:56:05 +02:00
return;
}
2020-06-15 16:56:05 +02:00
if(this.props.direction === "horizontal") {
const center = this.refSeparator.current.clientWidth;
previousElement.style.width = `calc(${this.value}% - ${center / 2}px)`;
nextElement.style.width = `calc(${100 - this.value}% - ${center / 2}px)`;
} else {
const center = this.refSeparator.current.clientHeight;
previousElement.style.height = `calc(${this.value}% - ${center / 2}px)`;
nextElement.style.height = `calc(${100 - this.value}% - ${center / 2}px)`;
}
}
}