adwaita-web
Version:
A GTK inspired toolkit designed to build awesome web apps
180 lines (179 loc) • 5.48 kB
JavaScript
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
};