UNPKG

adwaita-web

Version:

A GTK inspired toolkit designed to build awesome web apps

180 lines (179 loc) 5.48 kB
import cx from "clsx"; import React from "react"; import AutoSizer from "react-virtualized-auto-sizer"; import { trackFinger } from "../utils/trackFinger"; const properties = { horizontal: { size: "width", position: "left", event: "x" }, vertical: { size: "height", position: "top", event: "y" } }; class Paned extends React.Component { static defaultProps = { orientation: "horizontal", border: true }; handle; touchId; state; constructor(props) { super(props); this.handle = React.createRef(); this.touchId = void 0; this.state = { size: props.defaultSize, containerSize: void 0 }; } removeEventListeners = () => { document.removeEventListener("mousemove", this.onTouchMove); document.removeEventListener("mouseup", this.onTouchEnd); document.removeEventListener("touchmove", this.onTouchMove); document.removeEventListener("touchend", this.onTouchEnd); }; onTouchMove = (event) => { event.preventDefault(); const { orientation = "horizontal" } = this.props; const finger = trackFinger(event, { current: this.touchId }); if (!finger) { this.removeEventListeners(); return; } if (this.handle.current) { const handle = this.handle.current.getBoundingClientRect(); const handlePosition = handle[properties[orientation].position]; const mousePosition = finger[properties[orientation].event]; const delta = mousePosition - handlePosition; this.setState({ size: (this.state.size ?? 0) + delta }); } }; onTouchEnd = () => { this.setState({ dragging: false }); this.removeEventListeners(); }; onTouchStart = (event) => { event.preventDefault(); const touch = event.changedTouches[0]; this.touchId = touch?.identifier; this.setState({ dragging: true }); document.addEventListener("touchmove", this.onTouchMove); document.addEventListener("touchend", this.onTouchEnd); }; onMouseDown = (event) => { if (event.button !== 0) return; event.preventDefault(); this.setState({ dragging: true }); document.addEventListener("mousemove", this.onTouchMove); document.addEventListener("mouseup", this.onTouchEnd); }; onKeyDown = (ev) => { const { containerSize } = this.state; let size; switch (ev.key) { case "ArrowLeft": case "ArrowUp": size = (this.state.size ?? 0) - 4; break; case "ArrowRight": case "ArrowDown": size = (this.state.size ?? 0) + 4; break; default: return; } if (size < 0) size = 0; if (containerSize && size > containerSize) size = containerSize; this.setState({ size }); ev.preventDefault(); }; updateContainerSize(dimensions) { const { orientation = "horizontal" } = this.props; const containerSize = dimensions[properties[orientation].size]; if (this.state.size !== void 0 && this.state.containerSize === containerSize) return; setTimeout(() => { this.setState({ containerSize }); if (this.state.size === void 0) this.setState({ size: containerSize / 2 }); }, 0); } render() { const { children, className, orientation = "horizontal", border, fill, defaultSize, grow, ...rest } = this.props; const { size = 0 } = this.state; if (!Array.isArray(children) || children.length < 2) throw new Error("Paned: requires 2 children at least"); return /* @__PURE__ */ React.createElement("div", { className: cx("Paned", className, orientation, typeof grow === "number" ? `grow-${grow}` : grow ? "grow" : void 0, { fill: fill === true, "fill-width": fill === "width", "fill-height": fill === "height", "border-none": border === false, "border-handle": border === "handle" }), ...rest }, /* @__PURE__ */ React.createElement(AutoSizer, null, (dimensions) => { this.updateContainerSize(dimensions); return /* @__PURE__ */ React.createElement("div", { className: cx("Paned__wrapper", orientation), style: dimensions }, /* @__PURE__ */ React.createElement("div", { className: "Paned__pane", style: firstStyle(orientation, size) }, children[0]), /* @__PURE__ */ React.createElement("div", { className: "Paned__handle", role: "separator", "aria-orientation": orientation === "horizontal" ? "vertical" : "horizontal", tabIndex: 0, onMouseDown: this.onMouseDown, onTouchStart: this.onTouchStart, onKeyDown: this.onKeyDown, ref: this.handle, style: handleStyle(orientation, size) }), /* @__PURE__ */ React.createElement("div", { className: "Paned__pane", style: secondStyle(orientation, size, dimensions) }, children[1])); })); } } function handleStyle(orientation, size) { return { [properties[orientation].position]: size - 1 }; } function firstStyle(orientation, size) { return { [properties[orientation].size]: size }; } function secondStyle(orientation, size, dimensions) { const totalSize = dimensions[properties[orientation].size]; if (typeof totalSize !== "number" || typeof size !== "number") return void 0; const secondSize = totalSize - size; return { [properties[orientation].size]: secondSize }; } export { Paned, properties };