preact-arco-design
Version:
Arco Design React UI Library.
431 lines (359 loc) • 14.5 kB
JavaScript
var __read = this && this.__read || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o),
r,
ar = [],
e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) {
ar.push(r.value);
}
} catch (error) {
e = {
error: error
};
} finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
} finally {
if (e) throw e.error;
}
}
return ar;
};
var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
import React, { useState, useEffect, useRef, useContext, forwardRef, useImperativeHandle } from "preact/compat";
import { ConfigContext } from "../ConfigProvider";
import cs from "../_util/classNames";
import { isFunction, isNumber, isUndefined, isObject, isString } from "../_util/is";
import ResizeTrigger from "./resize-trigger";
import { on, off } from "../_util/dom";
var DIRECTION_HORIZONTAL = 'horizontal';
var DIRECTION_VERTICAL = 'vertical';
function SplitGroup(props, ref) {
var _a, _b;
var panes = props.panes,
style = props.style,
className = props.className,
_c = props.component,
component = _c === void 0 ? 'div' : _c,
_d = props.direction,
direction = _d === void 0 ? 'horizontal' : _d,
icon = props.icon;
var _e = useContext(ConfigContext),
getPrefixCls = _e.getPrefixCls,
rtl = _e.rtl;
var defaultOffset = 1 / panes.length;
var wrapperRef = useRef();
var recordRef = useRef(new Array(panes.length).fill({
moving: false,
startOffset: 0,
startPosition: 0
}));
var paneContainers = useRef([]);
var movingIndex = useRef(0);
var prevOffsets = useRef([]);
var _f = __read(useState(new Array(panes.length).fill(defaultOffset)), 2),
offsets = _f[0],
setOffsets = _f[1];
var _g = __read(useState(false), 2),
isMoving = _g[0],
setIsMoving = _g[1];
var _h = __read(useState(new Array(panes.length).fill(0)), 2),
triggerSize = _h[0],
setTriggerSize = _h[1];
var _j = __read(useState(new Array(Math.max(panes.length - 1, 0)).fill({
prev: false,
next: false
})), 2),
collapsedStatus = _j[0],
setCollapsedStatus = _j[1];
var prefixCls = getPrefixCls('resizebox-split-group');
var isHorizontal = direction === DIRECTION_HORIZONTAL;
var rtlReverse = isHorizontal && rtl;
var isTriggerHorizontal = !isHorizontal;
var classNames = cs(prefixCls, "".concat(prefixCls, "-").concat(isHorizontal ? DIRECTION_HORIZONTAL : DIRECTION_VERTICAL), (_a = {}, _a["".concat(prefixCls, "-moving")] = isMoving, _a), (_b = {}, _b["".concat(prefixCls, "-rtl")] = rtl, _b), className);
var Tag = component; // 获取初始的 offset, 将传入的size 都转为像素值。
var getInitialOffsets = function getInitialOffsets() {
var newOffsets = [];
panes.forEach(function (pane) {
var size = pane.size;
if (!isUndefined(size)) {
newOffsets.push(formatSize(size));
} else {
newOffsets.push(undefined);
}
}); // 剩余的空间均分给没有设置 size 的面板
var noSizeArr = newOffsets.filter(function (size) {
return !size;
});
var remainPercent = 1 - newOffsets.reduce(function (a, b) {
var formatA = a || 0;
var formatB = b || 0;
return formatA + formatB;
}, 0);
var averagePercent = remainPercent / noSizeArr.length;
newOffsets = newOffsets.map(function (size) {
if (!isUndefined(size)) {
return size;
}
return averagePercent;
});
return newOffsets;
}; // 计算每一个面板的占位像素,需要减去前面跟当前伸缩杆的宽度
var getPaneSize = function getPaneSize(index) {
var prevTriggerSize = triggerSize[index - 1] || 0;
var currentTriggerSize = triggerSize[index];
var baseVal = offsets[index] * 100;
var unit = '%';
return "calc(".concat(baseVal).concat(unit, " - ").concat((prevTriggerSize + currentTriggerSize) / 2, "px)");
}; // 入参 百分比/像素值 => 全部转化为百分比(响应式)
function formatSize(size) {
var totalPX = isHorizontal ? wrapperRef.current.offsetWidth : wrapperRef.current.offsetHeight;
if (!size || isNumber(size) && size < 0) {
return 0;
}
var percent = isString(size) ? parseFloat(size) / totalPX : size;
return Math.min(percent, 1);
} // 计算阈值,因为伸缩杆会影响到当前面板 跟 下一个面板。所以同时计算两个阈值。
var getMinAndMax = function getMinAndMax(index) {
var next = Math.min(index + 1, panes.length - 1);
var totalOffset = offsets[index] + offsets[next];
var currentMin = formatSize(panes[index].min) || 0;
var currentMax = formatSize(panes[index].max) || totalOffset;
var nextMin = formatSize(panes[next].min) || 0;
var nextMax = formatSize(panes[next].max) || totalOffset; // min 的优先级高于 max
currentMax = Math.min(totalOffset - nextMin, currentMax);
nextMax = Math.min(totalOffset - currentMin, nextMax);
return {
currentMin: currentMin,
currentMax: currentMax,
nextMin: nextMin,
nextMax: nextMax
};
}; // 拖拽时,获取新的占位距离。影响当前面板跟下一个面板的占位值。
var getNewOffsets = function getNewOffsets(startOffset, startPosition, currentPosition) {
var current = movingIndex.current;
var next = current + 1;
var newOffsets = __spreadArray([], __read(offsets), false);
var ratio = rtlReverse ? -1 : 1;
var currentPercent = offsets[current];
var nextPercent = offsets[next];
var totalPercent = currentPercent + nextPercent;
var _a = getMinAndMax(current),
minOffset = _a.currentMin,
maxOffset = _a.currentMax;
var moveOffset = startOffset + formatSize("".concat((currentPosition - startPosition) * ratio, "px"));
moveOffset = Math.max(minOffset, moveOffset);
moveOffset = Math.min(maxOffset, moveOffset);
newOffsets[current] = moveOffset; // 保证 totalOffset = nextOffset + currentOffset 不变。
newOffsets[next] = totalPercent - moveOffset;
return newOffsets;
};
function onTriggerResize(e, index) {
var contentRect = e[0].contentRect;
var currentSize = contentRect[isTriggerHorizontal ? 'height' : 'width'];
var newTriggerSize = __spreadArray([], __read(triggerSize), false);
newTriggerSize[index] = currentSize;
setTriggerSize(newTriggerSize);
} // 判断快速收缩按钮是否展示
var getCollapsedConfig = function getCollapsedConfig(index) {
var collapsible = panes[index].collapsible;
if (!isObject(collapsible)) {
collapsible = !collapsible ? {} : {
prev: true,
next: true
};
}
var prev = collapsible.prev,
next = collapsible.next;
if (!prev && !next) {
return {};
}
if (!collapsedStatus[index]) {
return {};
} // 传入了prev的配置,或者 没有传入 prev 的配置,但是已经处于向下收缩完毕状态
var hasPrev = !!prev || !prev && collapsedStatus[index].next; // 传入了next的配置,或者 没有传入 next 的配置,但是已经处于向上收缩完毕状态
var hasNext = !!next || !next && collapsedStatus[index].prev;
return {
hasPrev: hasPrev,
hasNext: hasNext
};
}; // 移动开始,记录初始值,绑定移动事件
function onTriggerMouseDown(e, index) {
props.onMovingStart && props.onMovingStart(index);
movingIndex.current = index;
var currentRecord = recordRef.current[index];
currentRecord.moving = true;
currentRecord.startOffset = offsets[index];
currentRecord.startPosition = isHorizontal ? e.pageX : e.pageY;
setIsMoving(true);
on(window, 'mousemove', moving);
on(window, 'touchmove', moving);
on(window, 'mouseup', moveEnd);
on(window, 'touchend', moveEnd);
on(window, 'contextmenu', moveEnd);
document.body.style.cursor = isTriggerHorizontal ? 'row-resize' : 'col-resize';
} // 移动中,更新 当前面板跟下一个面板 占位大小
function moving(e) {
var index = movingIndex.current;
var currentRecord = recordRef.current[index];
var totalPX = isHorizontal ? wrapperRef.current.offsetWidth : wrapperRef.current.offsetHeight;
if (currentRecord.moving) {
var newOffsets = getNewOffsets(currentRecord.startOffset, currentRecord.startPosition, isHorizontal ? e.pageX : e.pageY);
setOffsets(newOffsets);
prevOffsets.current = newOffsets;
props.onMoving && props.onMoving(e, newOffsets.map(function (value) {
return "".concat(value * totalPX, "px");
}), index);
}
} // 移动结束,解除事件绑定
function moveEnd() {
var index = movingIndex.current;
recordRef.current[index].moving = false;
setIsMoving(false);
off(window, 'mousemove', moving);
off(window, 'touchmove', moving);
off(window, 'mouseup', moveEnd);
off(window, 'touchend', moveEnd);
off(window, 'contextmenu', moveEnd);
document.body.style.cursor = 'default';
props.onMovingEnd && props.onMovingEnd(index);
} // 点击快速收缩按钮的回调。
function handleCollapsed(e, index, status, callback) {
var next = index + 1;
var newOffset = __spreadArray([], __read(offsets), false);
var currentOffset = offsets[index];
var nextOffset = offsets[next];
var totalOffset = currentOffset + nextOffset;
var totalPX = isHorizontal ? wrapperRef.current.offsetWidth : wrapperRef.current.offsetHeight;
var _a = getMinAndMax(index),
currentMin = _a.currentMin,
nextMin = _a.nextMin; // 取消收缩时,应该重置为上一个状态。所以从preOffsets里拿值
var newCurrentOffset = prevOffsets.current[index];
var newNextOffset = prevOffsets.current[next]; // 当前面板的收缩状态。
var collapsed = collapsedStatus[index][status]; // 点击向上收缩按钮。收缩态是:currentPane = currentMin;
if (status === 'prev') {
// 如果下一个面板不是在收缩状态 或者 下一个面板被手动拖拽到收缩状态
if (nextOffset !== nextMin || newNextOffset === nextMin) {
// 当前面板收缩。
newCurrentOffset = currentMin;
newNextOffset = totalOffset - currentMin;
collapsed = true;
} // 点击向下收缩按钮
} else if (currentOffset !== currentMin || newCurrentOffset === currentMin) {
newCurrentOffset = totalOffset - nextMin;
newNextOffset = nextMin;
collapsed = true;
}
newOffset[index] = newCurrentOffset;
newOffset[next] = newNextOffset;
props.onMoving && props.onMoving(e, newOffset.map(function (value) {
return "".concat(value * totalPX, "px");
}), index);
props.onMovingEnd && props.onMovingEnd(index);
setOffsets(newOffset);
if (isFunction(callback)) {
callback(e, index, status, collapsed);
}
}
useEffect(function () {
var offsets = getInitialOffsets();
setOffsets(offsets);
prevOffsets.current = offsets;
}, [JSON.stringify(panes.map(function (item) {
return item.size;
}))]);
useImperativeHandle(ref, function () {
return wrapperRef.current;
}, []);
useEffect(function () {
var newCollapsedStatus = [];
offsets.forEach(function (offset, index) {
var currentCollapsedStatus = {
prev: false,
next: false
};
var next = index + 1;
var _a = getMinAndMax(index),
currentMin = _a.currentMin,
nextMin = _a.nextMin; // 当 offsets 变化时,更新各个面板的 collapsed 状态
if (offset === currentMin) {
currentCollapsedStatus.prev = true;
} else if (offsets[next] === nextMin) {
currentCollapsedStatus.next = true;
}
newCollapsedStatus.push(currentCollapsedStatus);
});
setCollapsedStatus(newCollapsedStatus);
}, [offsets]);
return React.createElement(Tag, {
style: style,
className: classNames,
ref: wrapperRef
}, panes.map(function (pane, index) {
var content = pane.content,
disabled = pane.disabled,
trigger = pane.trigger,
_a = pane.resizable,
resizable = _a === void 0 ? true : _a,
_b = pane.collapsible,
collapsible = _b === void 0 ? {} : _b;
var _c = getCollapsedConfig(index),
hasPrev = _c.hasPrev,
hasNext = _c.hasNext;
var prevConfig = isObject(collapsible) && isObject(collapsible.prev) ? collapsible.prev : {};
var nextConfig = isObject(collapsible) && isObject(collapsible.next) ? collapsible.next : {};
return React.createElement(React.Fragment, {
key: index
}, React.createElement("div", {
className: "".concat(prefixCls, "-pane"),
style: {
flexBasis: getPaneSize(index)
},
ref: function ref(el) {
return paneContainers.current[index] = el;
}
}, content), !disabled && index !== panes.length - 1 && React.createElement(ResizeTrigger, {
className: "".concat(prefixCls, "-trigger"),
direction: isTriggerHorizontal ? DIRECTION_HORIZONTAL : DIRECTION_VERTICAL,
icon: icon,
onResize: function onResize(e) {
return onTriggerResize(e, index);
},
onMouseDown: function onMouseDown(e) {
return onTriggerMouseDown(e, index);
},
collapsible: {
prev: hasPrev ? {
onClick: function onClick(e) {
return handleCollapsed(e, index, 'prev', prevConfig.onClick);
},
icon: prevConfig.icon,
collapsed: collapsedStatus[index].prev
} : undefined,
next: hasNext ? {
onClick: function onClick(e) {
return handleCollapsed(e, index, 'next', nextConfig.onClick);
},
icon: nextConfig.icon,
collapsed: collapsedStatus[index].next
} : undefined
},
resizable: resizable,
renderChildren: trigger
}));
}));
}
var SplitGroupComponent = forwardRef(SplitGroup);
SplitGroupComponent.displayName = 'ResizeBoxSplitGroup';
export default SplitGroupComponent;