UNPKG

linkmore-design

Version:

🌈 🚀lm组件库。🚀

524 lines (504 loc) 19.8 kB
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; } };