You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
4.1 KiB
JavaScript

"use strict";
const React = require("react");
const useMeasure = require("react-use-measure");
const syncpipe = require("syncpipe");
const defaultValue = require("default-value");
const clamp = require("clamp");
const useTheme = require("../../util/themeable");
const defaultStyle = require("./style.css");
const childrenWithProps = require("../../util/children-with-props");
const generateGridTemplateString = require("../../util/generate-grid-template-string");
const useIndexedState = require("../../util/use-indexed-state");
const sum = require("../../util/sum");
const MINIMUM_PANE_SIZE = 16;
// FIXME: Logic to constrain resizing when increasing a pane's size further would make the paneset exceed its size boundaries
// FIXME: Disable selection globally during dragging
function selectSmallestSize(sizes) {
console.log("sizes", sizes);
if (sizes.some((size) => size != null && isNaN(size))) {
throw new Error(`NaN encountered!`);
}
return syncpipe(sizes, [
(_) => _.filter((size) => size != null),
(_) => (_.length > 0)
? Math.min(... _)
: undefined
]);
}
module.exports = function PaneSet({ vertical, horizontal, children }) {
let { withTheme } = useTheme({ control: "paneSet", defaultStyle });
let [ boundsRef, bounds ] = useMeasure({ debounce: 10 });
let [ newSizes, setNewSize, resetNewSizes ] = useIndexedState();
// FIXME: How to deal with new children? Reset all newSizes?
let direction = (vertical === true)
? "vertical"
: "horizontal";
let ownSize = (direction === "vertical")
? bounds.height
: bounds.width;
let childrenArray = React.Children.toArray(children);
let minimumSizes = React.useMemo(() => {
return childrenArray.map((child) => {
return selectSmallestSize([ child.props.minimumSize, MINIMUM_PANE_SIZE ]);
});
});
// FIXME: Detect when this fails
let autoPaneMinimumSize = syncpipe(childrenArray, [
(_) => _.findIndex((child) => child.resizable !== true),
(_) => minimumSizes[_]
]);
let maximumSizes = React.useMemo(() => {
let paneSizes = childrenArray.map((child, i) => {
// FIXME: Require initialSize when resizable
return defaultValue(newSizes[i], child.props.initialSize);
});
return childrenArray.map((_, ownIndex) => {
let otherPaneSizes = syncpipe(paneSizes, [
(_) => _.filter((size, i) => size != null && i !== ownIndex),
(_) => sum(_)
]);
return clamp(ownSize - otherPaneSizes - autoPaneMinimumSize, 0, Infinity);
});
}, [ childrenArray, newSizes, ownSize ]);
let autoSizeSeen = 0;
// NOTE: Not using React.Children.map because it will ignore null return values
let handleSides = childrenArray.map((child) => {
if (child.props.resizable === true) {
if (autoSizeSeen > 0) {
return (direction === "vertical")
? "n" // north, top
: "w"; // west, left
} else {
return (direction === "vertical")
? "s" // south, bottom
: "e"; // east, right
}
} else {
autoSizeSeen += 1;
return null;
}
});
let gridTemplateProperty = (direction === "vertical")
? "gridTemplateRows"
: "gridTemplateColumns";
let gridTemplateString = syncpipe(handleSides, [
(_) => _.map((handle) => handle != null ? "auto" : 1),
(_) => generateGridTemplateString(_)
]);
let style = {
[gridTemplateProperty]: gridTemplateString
};
if (autoSizeSeen !== 1) {
// We don't allow *multiple* auto-sized panes, to avoid weird behaviour where the auto-layouted panes may not end up exactly where the resize handle is
throw new Error(`Exactly one (center) pane must be non-resizable!`);
}
return (
<div ref={boundsRef} className={withTheme("uilibComponent", "paneSet", `direction-${direction}`)} style={style}>
{childrenWithProps(children, (child, i) => {
let handleSide = handleSides[i];
return {
onResized: (newSize) => {
setNewSize(i, newSize);
},
maximumSize: selectSmallestSize([ child.props.maximumSize, maximumSizes[i] ]),
// Note that we shouldn't override an explicitly user-configured handleSide
... (handleSide == null)
? {}
: {
handleSide: defaultValue(child.props.handleSide, handleSide),
direction: direction
}
};
})}
</div>
);
};