UNPKG

@lonli-lokli/react-mosaic-component

Version:
162 lines (161 loc) 5.14 kB
// 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 };