linkmore-design
Version:
🌈 🚀lm组件库。🚀
524 lines (504 loc) • 19.8 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/esm/extends";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
var _excluded = ["width", "onResize", "children"],
_excluded2 = ["children"],
_excluded3 = ["children", "style"],
_excluded4 = ["children"],
_excluded5 = ["style", "children"],
_excluded6 = ["width"];
import React, { useRef, useEffect, useContext, createContext, useReducer, useState, useMemo } from 'react';
import { throttle, isNumber } from 'lodash';
import { ResizableBox } from 'react-resizable';
import Context from "./context";
// ===============reducer ============== //
var initialState = {
// 行高度
rowHeight: 0,
// 当前的scrollTop
curScrollTop: 0,
// 总行数
totalLen: 0
};
function reducer(state, action) {
var curScrollTop = action.curScrollTop,
curScrollLeft = action.curScrollLeft,
rowHeight = action.rowHeight,
totalLen = action.totalLen,
ifScrollTopClear = action.ifScrollTopClear,
isScrolling = action.isScrolling;
var stateScrollTop = state.curScrollTop;
switch (action.type) {
// 滚动条是否滚动中
case 'changeScrolling':
return _objectSpread(_objectSpread({}, state), {}, {
isScrolling: isScrolling
});
case 'changeTrs':
return _objectSpread(_objectSpread({}, state), {}, {
curScrollTop: curScrollTop
});
// 滚动横向滚动条 即 改变渲染的列表operate
case 'changeOperate':
return _objectSpread(_objectSpread({}, state), {}, {
curScrollLeft: curScrollLeft
});
// 初始化每行的高度, 表格总高度, 渲染的条数
case 'initHeight':
return _objectSpread(_objectSpread({}, state), {}, {
rowHeight: rowHeight
});
// 更改totalLen
case 'changeTotalLen':
if (totalLen === 0) {
stateScrollTop = 0;
}
return _objectSpread(_objectSpread({}, state), {}, {
totalLen: totalLen,
curScrollTop: stateScrollTop
});
case 'reset':
return _objectSpread(_objectSpread({}, state), {}, {
curScrollTop: ifScrollTopClear ? 0 : state.curScrollTop
});
default:
throw new Error();
}
}
// ============== 全局常量 ================== //
var DEFAULT_VID = 'vtable';
var vidMap = new Map();
// =============== context ============== //
var ScrollContext = /*#__PURE__*/createContext({
dispatch: undefined,
renderLen: 1,
start: 0,
offsetStart: 0,
// =============
rowHeight: initialState.rowHeight,
totalLen: 0,
vid: DEFAULT_VID
});
var HeaderContext = /*#__PURE__*/createContext();
// ============= 组件 =================== //
var HeaderWrapper = function HeaderWrapper(props) {
var _useState = useState(),
_useState2 = _slicedToArray(_useState, 2),
useKeys = _useState2[0],
setKeys = _useState2[1];
var _useContext = useContext(Context),
width = _useContext.width;
var changeKeys = function changeKeys() {
setKeys(Date.now());
};
useEffect(function () {
changeKeys();
}, [width]);
// console.log('每列的偏移', props.children[0].props.stickyOffsets);
return /*#__PURE__*/React.createElement(HeaderContext.Provider, {
value: {
changeKeys: changeKeys
}
}, /*#__PURE__*/React.createElement("thead", _extends({
key: useKeys
}, props)));
};
var HeaderCell = function HeaderCell(props) {
var width = props.width,
onResize = props.onResize,
children = props.children,
restProps = _objectWithoutProperties(props, _excluded);
var _useState3 = useState(width || 0),
_useState4 = _slicedToArray(_useState3, 2),
useWidth = _useState4[0],
setWidth = _useState4[1];
var _useContext2 = useContext(HeaderContext),
changeKeys = _useContext2.changeKeys;
var handleResize = function handleResize(e, _ref) {
var size = _ref.size;
var nValue = size.width;
setWidth(nValue);
};
var handleResizeStop = function handleResizeStop(e, _ref2) {
var size = _ref2.size;
var nValue = size.width;
// const sc = width - nValue > 0 ? width - nValue : 1;
onResize === null || onResize === void 0 ? void 0 : onResize(width, nValue);
// 触发重新渲染列头单元格,重新计算拖拽位置
changeKeys === null || changeKeys === void 0 ? void 0 : changeKeys();
};
if (!width) {
return /*#__PURE__*/React.createElement("th", restProps, /*#__PURE__*/React.createElement("div", {
className: "text-overflow"
}, children));
}
return /*#__PURE__*/React.createElement("th", restProps, /*#__PURE__*/React.createElement("div", {
className: "text-overflow"
}, children), /*#__PURE__*/React.createElement(ResizableBox, {
className: "yf_resizable_bar",
width: useWidth,
height: 0,
handle: /*#__PURE__*/React.createElement("div", {
className: "react-resizable-handle",
onClick: function onClick(e) {
e.stopPropagation();
}
}),
onResize: handleResize,
onResizeStop: handleResizeStop,
minConstraints: [40, 0]
// draggableOpts={{ enableUserSelectHack: false }}
}));
};
var VCell = function VCell(props) {
var children = props.children,
restProps = _objectWithoutProperties(props, _excluded2);
return /*#__PURE__*/React.createElement("td", restProps, children);
};
var VRow = function VRow(props, ref) {
var _useContext3 = useContext(ScrollContext),
dispatch = _useContext3.dispatch,
rowHeight = _useContext3.rowHeight,
totalLen = _useContext3.totalLen,
vid = _useContext3.vid;
var children = props.children,
style = props.style,
restProps = _objectWithoutProperties(props, _excluded3);
var trRef = useRef(null);
useEffect(function () {
var initHeight = function initHeight(tempRef) {
var _tempRef$current;
if (tempRef !== null && tempRef !== void 0 && (_tempRef$current = tempRef.current) !== null && _tempRef$current !== void 0 && _tempRef$current.offsetHeight && !rowHeight && totalLen) {
var _tempRef$current$offs, _tempRef$current2;
var tempRowHeight = (_tempRef$current$offs = tempRef === null || tempRef === void 0 ? void 0 : (_tempRef$current2 = tempRef.current) === null || _tempRef$current2 === void 0 ? void 0 : _tempRef$current2.offsetHeight) !== null && _tempRef$current$offs !== void 0 ? _tempRef$current$offs : 0;
vidMap.set(vid, _objectSpread(_objectSpread({}, vidMap.get(vid)), {}, {
rowItemHeight: tempRowHeight
}));
dispatch({
type: 'initHeight',
rowHeight: tempRowHeight
});
}
};
initHeight(Object.prototype.hasOwnProperty.call(ref, 'current') ? ref : trRef);
}, [trRef, dispatch, rowHeight, totalLen, ref, vid]);
return /*#__PURE__*/React.createElement("tr", _extends({}, restProps, {
ref: Object.prototype.hasOwnProperty.call(ref, 'current') ? ref : trRef,
style: _objectSpread(_objectSpread({}, style), {}, {
height: rowHeight || 'auto',
boxSizing: 'border-box'
})
}), children);
};
var VWrapper = function VWrapper(props) {
var children = props.children,
restProps = _objectWithoutProperties(props, _excluded4);
var _useContext4 = useContext(ScrollContext),
renderLen = _useContext4.renderLen,
start = _useContext4.start,
dispatch = _useContext4.dispatch,
vid = _useContext4.vid,
totalLen = _useContext4.totalLen;
var contents = useMemo(function () {
return children[1];
}, [children]);
var contentsLen = useMemo(function () {
var _contents$length;
return (_contents$length = contents === null || contents === void 0 ? void 0 : contents.length) !== null && _contents$length !== void 0 ? _contents$length : 0;
}, [contents]);
useEffect(function () {
if (totalLen !== contentsLen) {
dispatch({
type: 'changeTotalLen',
totalLen: contentsLen !== null && contentsLen !== void 0 ? contentsLen : 0
});
}
}, [contentsLen, dispatch, vid, totalLen]);
var tempNode = null;
if (Array.isArray(contents) && contents.length) {
tempNode = [children[0], contents.slice(start, start + (renderLen !== null && renderLen !== void 0 ? renderLen : 1))
// contents.slice(start, start + (renderLen ?? 1)).map((item) => {
// if (Array.isArray(item)) {
// // 兼容antd v4.3.5 --- rc-table 7.8.1及以下
// return item[0];
// }
// // 处理antd ^v4.4.0 --- rc-table ^7.8.2
// return item;
// }),
];
} else {
tempNode = children;
}
return /*#__PURE__*/React.createElement("tbody", restProps, tempNode);
};
var VTable = function VTable(props, otherParams) {
var _children$1$props$dat, _children$, _children$$props, _children$$props$data;
var style = props.style,
children = props.children,
rest = _objectWithoutProperties(props, _excluded5);
var width = style.width,
restStyle = _objectWithoutProperties(style, _excluded6);
var _ref3 = otherParams !== null && otherParams !== void 0 ? otherParams : {},
vid = _ref3.vid,
scrollY = _ref3.scrollY,
reachEnd = _ref3.reachEnd,
onScroll = _ref3.onScroll,
resetScrollTopWhenDataChange = _ref3.resetScrollTopWhenDataChange;
var _useReducer = useReducer(reducer, initialState),
_useReducer2 = _slicedToArray(_useReducer, 2),
state = _useReducer2[0],
dispatch = _useReducer2[1];
var wrapTableRef = useRef(null);
var tableRef = useRef(null);
var ifChangeRef = useRef(false);
// 数据的总条数
var _useState5 = useState((_children$1$props$dat = (_children$ = children[1]) === null || _children$ === void 0 ? void 0 : (_children$$props = _children$.props) === null || _children$$props === void 0 ? void 0 : (_children$$props$data = _children$$props.data) === null || _children$$props$data === void 0 ? void 0 : _children$$props$data.length) !== null && _children$1$props$dat !== void 0 ? _children$1$props$dat : 0),
_useState6 = _slicedToArray(_useState5, 2),
totalLen = _useState6[0],
setTotalLen = _useState6[1];
useEffect(function () {
setTotalLen(state.totalLen);
}, [state.totalLen]);
useEffect(function () {
return function () {
vidMap.delete(vid);
};
}, [vid]);
// 数据变更
useEffect(function () {
var _children$2, _children$2$props, _children$2$props$dat;
ifChangeRef.current = true;
if (isNumber((_children$2 = children[1]) === null || _children$2 === void 0 ? void 0 : (_children$2$props = _children$2.props) === null || _children$2$props === void 0 ? void 0 : (_children$2$props$dat = _children$2$props.data) === null || _children$2$props$dat === void 0 ? void 0 : _children$2$props$dat.length)) {
var _children$1$props$dat2, _children$3, _children$3$props, _children$3$props$dat;
dispatch({
type: 'changeTotalLen',
totalLen: (_children$1$props$dat2 = (_children$3 = children[1]) === null || _children$3 === void 0 ? void 0 : (_children$3$props = _children$3.props) === null || _children$3$props === void 0 ? void 0 : (_children$3$props$dat = _children$3$props.data) === null || _children$3$props$dat === void 0 ? void 0 : _children$3$props$dat.length) !== null && _children$1$props$dat2 !== void 0 ? _children$1$props$dat2 : 0
});
}
}, [children[1].props.data]);
// table总高度
var tableHeight = useMemo(function () {
var temp = 'auto';
if (state.rowHeight && totalLen) {
temp = state.rowHeight * totalLen;
}
return temp;
}, [state.rowHeight, totalLen]);
// table的scrollY值
var _useState7 = useState(0),
_useState8 = _slicedToArray(_useState7, 2),
tableScrollY = _useState8[0],
setTableScrollY = _useState8[1];
// tableScrollY 随scrollY / tableHeight 进行变更
useEffect(function () {
var temp = 0;
if (typeof scrollY === 'string') {
var _wrapTableRef$current, _wrapTableRef$current2, _wrapTableRef$current3;
temp = (_wrapTableRef$current = (_wrapTableRef$current2 = wrapTableRef.current) === null || _wrapTableRef$current2 === void 0 ? void 0 : (_wrapTableRef$current3 = _wrapTableRef$current2.parentNode) === null || _wrapTableRef$current3 === void 0 ? void 0 : _wrapTableRef$current3.offsetHeight) !== null && _wrapTableRef$current !== void 0 ? _wrapTableRef$current : 0;
} else {
temp = scrollY;
}
// if (isNumber(tableHeight) && tableHeight < temp) {
// temp = tableHeight;
// }
// 处理tableScrollY <= 0的情况
if (temp <= 0) {
temp = 0;
}
setTableScrollY(temp);
}, [scrollY, tableHeight]);
// 渲染的条数
var renderLen = useMemo(function () {
var temp = 1;
if (state.rowHeight && totalLen && tableScrollY) {
if (tableScrollY <= 0) {
temp = 0;
} else {
var tempRenderLen = (tableScrollY / state.rowHeight || 0) + 1 + 20;
// temp = tempRenderLen > totalLen ? totalLen : tempRenderLen;
temp = tempRenderLen;
}
}
return temp;
}, [state.rowHeight, totalLen, tableScrollY]);
// 渲染中的第一条
var start = state.rowHeight ? state.curScrollTop / state.rowHeight || 0 : 0;
// 偏移量
var offsetStart = state.rowHeight ? state.curScrollTop % state.rowHeight : 0;
// 用来优化向上滚动出现的空白
if (state.curScrollTop && state.rowHeight && state.curScrollTop > state.rowHeight) {
start -= 1;
offsetStart += state.rowHeight;
} else {
start = 0;
}
// 数据变更 操作scrollTop
useEffect(function () {
var _wrapTableRef$current4;
var scrollNode = (_wrapTableRef$current4 = wrapTableRef.current) === null || _wrapTableRef$current4 === void 0 ? void 0 : _wrapTableRef$current4.parentNode;
if (ifChangeRef !== null && ifChangeRef !== void 0 && ifChangeRef.current) {
ifChangeRef.current = false;
if (resetScrollTopWhenDataChange) {
// 重置scrollTop
if (scrollNode) {
scrollNode.scrollTop = 0;
}
dispatch({
type: 'reset',
ifScrollTopClear: true
});
} else {
// 不重置scrollTop 不清空curScrollTop
dispatch({
type: 'reset',
ifScrollTopClear: false
});
}
}
if (vidMap.has(vid)) {
vidMap.set(vid, {
scrollNode: scrollNode
});
}
}, [totalLen, resetScrollTopWhenDataChange, vid, children]);
useEffect(function () {
var _wrapTableRef$current5;
var timer = null;
var timeout = 100;
var throttleScroll = throttle(function (e) {
var _e$target$scrollLeft, _e$target, _e$target$scrollTop, _e$target2, _e$target$scrollHeigh, _e$target3, _e$target$clientHeigh, _e$target4;
var scrollLeft = (_e$target$scrollLeft = e === null || e === void 0 ? void 0 : (_e$target = e.target) === null || _e$target === void 0 ? void 0 : _e$target.scrollLeft) !== null && _e$target$scrollLeft !== void 0 ? _e$target$scrollLeft : 0;
var scrollTop = (_e$target$scrollTop = e === null || e === void 0 ? void 0 : (_e$target2 = e.target) === null || _e$target2 === void 0 ? void 0 : _e$target2.scrollTop) !== null && _e$target$scrollTop !== void 0 ? _e$target$scrollTop : 0;
var scrollHeight = (_e$target$scrollHeigh = e === null || e === void 0 ? void 0 : (_e$target3 = e.target) === null || _e$target3 === void 0 ? void 0 : _e$target3.scrollHeight) !== null && _e$target$scrollHeigh !== void 0 ? _e$target$scrollHeigh : 0;
var clientHeight = (_e$target$clientHeigh = e === null || e === void 0 ? void 0 : (_e$target4 = e.target) === null || _e$target4 === void 0 ? void 0 : _e$target4.clientHeight) !== null && _e$target$clientHeigh !== void 0 ? _e$target$clientHeigh : 0;
// 到底了 没有滚动条就不会触发reachEnd. 建议设置scrolly高度少点或者数据量多点.
if (scrollTop === scrollHeight) {
// reachEnd && reachEnd()
} else if (scrollTop + clientHeight >= scrollHeight) {
// 有滚动条的情况
reachEnd && reachEnd();
}
onScroll && onScroll(scrollTop, scrollLeft);
if (!timer) {
dispatch({
type: 'changeScrolling',
isScrolling: true
});
} else {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function () {
dispatch({
type: 'changeScrolling',
isScrolling: false
});
timer = null;
}, timeout);
dispatch({
type: 'changeScrolling',
isScrolling: true
});
dispatch({
type: 'changeTrs',
curScrollTop: scrollTop
});
dispatch({
type: 'changeOperate',
curScrollLeft: scrollLeft
});
}, 60);
var ref = wrapTableRef === null || wrapTableRef === void 0 ? void 0 : (_wrapTableRef$current5 = wrapTableRef.current) === null || _wrapTableRef$current5 === void 0 ? void 0 : _wrapTableRef$current5.parentNode;
if (ref) {
ref.addEventListener('scroll', throttleScroll);
}
return function () {
ref.removeEventListener('scroll', throttleScroll);
};
}, [onScroll, reachEnd]);
return /*#__PURE__*/React.createElement("div", {
className: "virtuallist",
ref: wrapTableRef,
style: {
width: '100%',
position: 'relative',
height: tableHeight,
boxSizing: 'border-box',
paddingTop: state.curScrollTop
}
}, /*#__PURE__*/React.createElement(ScrollContext.Provider, {
value: {
dispatch: dispatch,
rowHeight: state.rowHeight,
start: start,
offsetStart: offsetStart,
scrollLeft: state.curScrollLeft,
renderLen: renderLen,
totalLen: totalLen,
vid: vid
}
}, /*#__PURE__*/React.createElement("table", _extends({}, rest, {
ref: tableRef,
style: _objectSpread(_objectSpread({}, restStyle), {}, {
width: width,
position: 'relative',
transform: "translateY(-".concat(offsetStart, "px)"),
pointerEvents: state.isScrolling ? 'none' : 'initial'
})
}), children)));
};
// ================导出===================
export var VList = function VList(props) {
var _props$vid = props.vid,
vid = _props$vid === void 0 ? DEFAULT_VID : _props$vid,
height = props.height,
onReachEnd = props.onReachEnd,
onScroll = props.onScroll,
_props$resetTopWhenDa = props.resetTopWhenDataChange,
resetTopWhenDataChange = _props$resetTopWhenDa === void 0 ? true : _props$resetTopWhenDa;
var resetScrollTopWhenDataChange = onReachEnd ? false : resetTopWhenDataChange;
if (!vidMap.has(vid)) {
vidMap.set(vid, {});
}
return {
table: function table(p) {
return VTable(p, {
vid: vid,
scrollY: height,
reachEnd: onReachEnd,
onScroll: onScroll,
resetScrollTopWhenDataChange: resetScrollTopWhenDataChange
});
},
body: {
wrapper: VWrapper,
row: VRow,
cell: function cell(cellProps) {
return /*#__PURE__*/React.createElement(VCell, cellProps);
}
},
header: {
wrapper: HeaderWrapper,
cell: HeaderCell
}
};
};
export var scrollTo = function scrollTo(option) {
var row = option.row,
y = option.y,
_option$vid = option.vid,
vid = _option$vid === void 0 ? DEFAULT_VID : _option$vid;
var _vidMap$get = vidMap.get(vid),
scrollNode = _vidMap$get.scrollNode,
rowItemHeight = _vidMap$get.rowItemHeight;
if (row) {
if (row - 1 > 0) {
scrollNode.scrollTop = (row - 1) * (rowItemHeight !== null && rowItemHeight !== void 0 ? rowItemHeight : 0);
} else {
scrollNode.scrollTop = 0;
}
} else {
scrollNode.scrollTop = y !== null && y !== void 0 ? y : 0;
}
};