UNPKG

split-pane-react-test

Version:
172 lines (171 loc) 7.44 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React, { useEffect, useMemo, useCallback, useRef, useState } from 'react'; import { Children, cloneElement, isValidElement } from 'react'; import Pane from './pane'; import Sash from './sash'; import { classNames, paneClassName as paneItemClassName, splitClassName, splitDragClassName, splitVerticalClassName, splitHorizontalClassName, sashDisabledClassName, sashHorizontalClassName, sashVerticalClassName } from './base'; /** * Clone react children props * @param children React.ReactNode * @param props Parent props */ function cloneReactChildren(children, props) { return Children.map(children, (child) => { if (isValidElement(child)) { return cloneElement(child, props); } return child; }); } /** * Convert size to absolute number or Infinity */ const assertsSize = function (size, sum, defaultValue = Infinity) { if (typeof size === 'undefined') return defaultValue; if (typeof size === 'number') return size; if (size.endsWith('%')) return sum * (+size.replace('%', '') / 100); if (size.endsWith('px')) return +size.replace('px', ''); return defaultValue; }; const SplitPane = (_a) => { var _b; var { children, sizes: propSizes, allowResize = true, split = 'vertical', className, sashClassName, paneClassName, resizerSize = 4, onChange } = _a, others = __rest(_a, ["children", "sizes", "allowResize", "split", "className", "sashClassName", "paneClassName", "resizerSize", "onChange"]); const axis = useRef({ x: 0, y: 0 }); const wrapper = useRef(null); const [wrapperRect, setWrapperRect] = useState({}); const [draging, setDrag] = useState(false); useEffect(() => { const resizeObserver = new ResizeObserver(() => { setWrapperRect(wrapper.current.getBoundingClientRect()); }); resizeObserver.observe(wrapper.current); return () => { resizeObserver.disconnect(); }; }, []); // Get some size infos via split const { sizeName, sPos, sAxis } = useMemo(function () { return { sizeName: split === 'vertical' ? 'width' : 'height', sPos: split === 'vertical' ? 'left' : 'top', sAxis: split === 'vertical' ? 'x' : 'y' }; }, [split]); const wrapSize = (_b = wrapperRect[sizeName]) !== null && _b !== void 0 ? _b : 0; // Get limit sizes via children const paneLimitSizes = useMemo(function () { return children.map(childNode => { const limits = [0, Infinity]; if (childNode.type === Pane) { const { minSize, maxSize } = childNode.props; limits[0] = assertsSize(minSize, wrapSize, 0); limits[1] = assertsSize(maxSize, wrapSize); } return limits; }); }, [children, wrapSize]); /** * SplitPane allows sizes in string and number, but the state sizes only support number, * so convert string and number to number in here * ```ts * 'auto' -> divide the remaining space equally * 'xxxpx' -> xxx * 'xxx%' -> wrapper.size * xxx/100 * xxx -> xxx * ``` */ const sizes = useMemo(function () { let count = 0; let curSum = 0; const res = children.map((_, index) => { const size = assertsSize(propSizes[index], wrapSize); size === Infinity ? count++ : curSum += size; return size; }); // resize or illegal size input,recalculate pane sizes if (curSum > wrapSize || !count && curSum < wrapSize) { const cacheNum = (curSum - wrapSize) / curSum; return res.map(size => { return size === Infinity ? 0 : size - size * cacheNum; }); } if (count > 0) { const average = (wrapSize - curSum) / count; return res.map(size => { return size === Infinity ? average : size; }); } return res; }, [...propSizes, children.length, wrapSize]); // Gets dragging axis position const sashPosSizes = useMemo(function () { return sizes.reduce(function (a, b) { return [...a, a[a.length - 1] + b]; }, [0]); }, [...sizes]); const onDragStart = useCallback(function (e) { axis.current = { x: e.pageX, y: e.pageY }; setDrag(true); }, []); const onDragging = useCallback(function (e, i) { const curAxis = { x: e.pageX, y: e.pageY }; let distanceX = curAxis[sAxis] - axis.current[sAxis]; const leftBorder = -Math.min(sizes[i] - paneLimitSizes[i][0], paneLimitSizes[i + 1][1] - sizes[i + 1]); const rightBorder = Math.min(sizes[i + 1] - paneLimitSizes[i + 1][0], paneLimitSizes[i][1] - sizes[i]); if (distanceX < leftBorder) { distanceX = leftBorder; } if (distanceX > rightBorder) { distanceX = rightBorder; } const nextSizes = [...sizes]; nextSizes[i] += distanceX; nextSizes[i + 1] -= distanceX; onChange(nextSizes); }, [paneLimitSizes, onChange]); return (React.createElement("div", Object.assign({ className: classNames(splitClassName, draging && splitDragClassName, split === 'vertical' && splitVerticalClassName, split === 'horizontal' && splitHorizontalClassName, className), ref: wrapper }, others), children.map((childNode, childIndex) => { const paneClasses = classNames(paneItemClassName, paneClassName); const paneStyle = { [sizeName]: sizes[childIndex], [sPos]: sashPosSizes[childIndex] }; let sashChild = null; if (childIndex > 0) { sashChild = (React.createElement(Sash, { className: classNames(!allowResize && sashDisabledClassName, split === 'vertical' ? sashVerticalClassName : sashHorizontalClassName, sashClassName), style: { [sizeName]: resizerSize, [sPos]: sashPosSizes[childIndex] - resizerSize / 2 }, onDragStart: onDragStart, onDragging: e => onDragging(e, childIndex - 1), onDragEnd: () => { setDrag(false); } })); } if (childNode.type === Pane) { const { className = '', style = {} } = childNode.props; return (React.createElement(React.Fragment, { key: childIndex }, sashChild, cloneReactChildren(childNode, { className: classNames(paneClasses, className), style: Object.assign(Object.assign({}, style), paneStyle) }))); } return (React.createElement(React.Fragment, { key: childIndex }, sashChild, React.createElement(Pane, { className: paneClasses, style: paneStyle }, childNode))); }))); }; export default SplitPane;