@lonli-lokli/react-mosaic-component
Version:
A React Tiling Window Manager
162 lines (161 loc) • 5.14 kB
JavaScript
// libs/react-mosaic-component/src/lib/Split.tsx
import classNames from "classnames";
import { clamp, sum } from "lodash-es";
import { throttle } from "lodash-es";
import React from "react";
import {
boundingBoxAsStyles,
getAbsoluteSplitPercentage,
getRelativeSplitPercentage
} from "./util/BoundingBox.mjs";
var RESIZE_THROTTLE_MS = 1e3 / 30;
var TOUCH_EVENT_OPTIONS = {
capture: true,
passive: false
};
var Split = class extends React.PureComponent {
rootElement = React.createRef();
listenersBound = false;
static defaultProps = {
onChange: () => void 0,
onRelease: () => void 0,
minimumPaneSizePercentage: 10
// Updated default
};
render() {
const { direction } = this.props;
return /* @__PURE__ */ React.createElement(
"div",
{
className: classNames("mosaic-split", {
"-row": direction === "row",
"-column": direction === "column"
}),
ref: this.rootElement,
onMouseDown: this.onMouseDown,
style: this.computeStyle()
},
/* @__PURE__ */ React.createElement("div", { className: "mosaic-split-line" })
);
}
componentDidMount() {
this.rootElement.current.addEventListener(
"touchstart",
this.onMouseDown,
TOUCH_EVENT_OPTIONS
);
}
componentWillUnmount() {
this.unbindListeners();
if (this.rootElement.current) {
this.rootElement.current.ownerDocument.removeEventListener(
"touchstart",
this.onMouseDown,
TOUCH_EVENT_OPTIONS
);
}
}
computeStyle() {
const { boundingBox, direction, splitPercentages, splitIndex } = this.props;
const relativeSplitterPosition = sum(
splitPercentages.slice(0, splitIndex + 1)
);
const absolutePercentage = getAbsoluteSplitPercentage(
boundingBox,
relativeSplitterPosition,
direction
);
const positionStyle = direction === "column" ? "top" : "left";
return {
...boundingBoxAsStyles(boundingBox),
[positionStyle]: `${absolutePercentage}%`
};
}
onMouseDown = (event) => {
if (!isTouchEvent(event) && event.button !== 0) return;
event.preventDefault();
this.bindListeners();
};
onMouseUp = (event) => {
this.unbindListeners();
const newPercentages = this.calculateNewPercentages(event);
this.props.onRelease(newPercentages);
};
onMouseMove = (event) => {
event.preventDefault();
this.throttledUpdatePercentage(event);
};
throttledUpdatePercentage = throttle(
(event) => {
const newPercentages = this.calculateNewPercentages(event);
this.props.onChange(newPercentages);
},
RESIZE_THROTTLE_MS
);
calculateNewPercentages(event) {
const {
minimumPaneSizePercentage,
direction,
boundingBox,
splitPercentages,
splitIndex
} = this.props;
const parentBBox = this.rootElement.current.parentElement.getBoundingClientRect();
const location = isTouchEvent(event) ? event.changedTouches[0] : event;
let mouseAbsolutePercentage;
if (direction === "column") {
mouseAbsolutePercentage = (location.clientY - parentBBox.top) / parentBBox.height * 100;
} else {
mouseAbsolutePercentage = (location.clientX - parentBBox.left) / parentBBox.width * 100;
}
const mouseRelativePercentage = getRelativeSplitPercentage(
boundingBox,
mouseAbsolutePercentage,
direction
);
const startPercentage = sum(splitPercentages.slice(0, splitIndex));
const totalSizeOfPanes = splitPercentages[splitIndex] + splitPercentages[splitIndex + 1];
let newLeftPaneSize = mouseRelativePercentage - startPercentage;
newLeftPaneSize = clamp(
newLeftPaneSize,
minimumPaneSizePercentage,
totalSizeOfPanes - minimumPaneSizePercentage
);
const newRightPaneSize = totalSizeOfPanes - newLeftPaneSize;
const newSplitPercentages = [...splitPercentages];
newSplitPercentages[splitIndex] = newLeftPaneSize;
newSplitPercentages[splitIndex + 1] = newRightPaneSize;
return newSplitPercentages;
}
// These bindings can remain as they were
bindListeners() {
if (!this.listenersBound) {
const doc = this.rootElement.current.ownerDocument;
doc.addEventListener("mousemove", this.onMouseMove, true);
doc.addEventListener("touchmove", this.onMouseMove, TOUCH_EVENT_OPTIONS);
doc.addEventListener("mouseup", this.onMouseUp, true);
doc.addEventListener("touchend", this.onMouseUp, true);
this.listenersBound = true;
}
}
unbindListeners() {
if (this.listenersBound && this.rootElement.current) {
const doc = this.rootElement.current.ownerDocument;
doc.removeEventListener("mousemove", this.onMouseMove, true);
doc.removeEventListener(
"touchmove",
this.onMouseMove,
TOUCH_EVENT_OPTIONS
);
doc.removeEventListener("mouseup", this.onMouseUp, true);
doc.removeEventListener("touchend", this.onMouseUp, true);
this.listenersBound = false;
}
}
};
function isTouchEvent(event) {
return event.changedTouches != null;
}
export {
Split
};