UNPKG

17.9 kBJavaScriptView Raw
1import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
2import _extends from 'babel-runtime/helpers/extends';
3import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
4import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
5import _inherits from 'babel-runtime/helpers/inherits';
6import React, { Children } from 'react';
7import { findDOMNode } from 'react-dom';
8import PropTypes from 'prop-types';
9import classnames from 'classnames';
10import shallowElementEquals from 'shallow-element-equals';
11import { log, obj, dom, events } from '../util';
12import LockRow from './lock/row';
13import LockBody from './lock/body';
14import LockHeader from './lock/header';
15import LockWrapper from './fixed/wrapper';
16import { statics, setStickyStyle } from './util';
17
18export default function stickyLock(BaseComponent) {
19 var _class, _temp;
20
21 /** Table */
22 var LockTable = (_temp = _class = function (_React$Component) {
23 _inherits(LockTable, _React$Component);
24
25 function LockTable(props, context) {
26 _classCallCheck(this, LockTable);
27
28 var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
29
30 _this.state = {};
31
32 _this.updateOffsetArr = function () {
33 var _ref = _this.splitChildren || {},
34 lockLeftChildren = _ref.lockLeftChildren,
35 lockRightChildren = _ref.lockRightChildren,
36 originChildren = _ref.originChildren;
37
38 var leftLen = _this.getFlatenChildren(lockLeftChildren).length;
39 var rightLen = _this.getFlatenChildren(lockRightChildren).length;
40 var totalLen = leftLen + rightLen + _this.getFlatenChildren(originChildren).length;
41
42 var hasLockLeft = leftLen > 0;
43 var hasLockRight = rightLen > 0;
44
45 var leftOffsetArr = _this.getStickyWidth(lockLeftChildren, 'left', totalLen);
46 var rightOffsetArr = _this.getStickyWidth(lockRightChildren, 'right', totalLen);
47
48 var state = {};
49
50 if ('' + leftOffsetArr !== '' + _this.state.leftOffsetArr) {
51 state.leftOffsetArr = leftOffsetArr;
52 }
53
54 if ('' + rightOffsetArr !== '' + _this.state.rightOffsetArr) {
55 state.rightOffsetArr = rightOffsetArr;
56 }
57
58 if (hasLockLeft !== _this.state.hasLockLeft) {
59 state.hasLockLeft = hasLockLeft;
60 }
61
62 if (hasLockRight !== _this.state.hasLockRight) {
63 state.hasLockRight = hasLockRight;
64 }
65
66 if (Object.keys(state).length > 0) {
67 _this.setState(state);
68 }
69 };
70
71 _this.onLockBodyScroll = function (e, forceSet) {
72 var _ref2 = e.currentTarget || {},
73 scrollLeft = _ref2.scrollLeft,
74 scrollWidth = _ref2.scrollWidth,
75 clientWidth = _ref2.clientWidth;
76
77 var pingRight = _this.pingRight,
78 pingLeft = _this.pingLeft;
79
80
81 var pingLeftNext = scrollLeft > 0 && _this.state.hasLockLeft;
82 var pingRightNext = scrollLeft < scrollWidth - clientWidth && _this.state.hasLockRight;
83
84 if (forceSet || pingLeft !== pingLeftNext || pingRight !== pingRightNext) {
85 var prefix = _this.props.prefix;
86
87 var table = _this.getTableNode();
88
89 _this.pingLeft = pingLeftNext;
90 _this.pingRight = pingRightNext;
91
92 var leftFunc = pingLeftNext ? 'addClass' : 'removeClass';
93 dom[leftFunc](table, prefix + 'table-ping-left');
94 var rightFunc = pingRightNext ? 'addClass' : 'removeClass';
95 dom[rightFunc](table, prefix + 'table-ping-right');
96 }
97 };
98
99 _this.getStickyWidth = function (lockChildren, dir, totalLen) {
100 var _this$props = _this.props,
101 dataSource = _this$props.dataSource,
102 scrollToRow = _this$props.scrollToRow;
103
104 var offsetArr = [];
105 var flatenChildren = _this.getFlatenChildren(lockChildren);
106 var len = flatenChildren.length;
107
108 flatenChildren.reduce(function (ret, col, index) {
109 var tag = dir === 'left' ? index : len - 1 - index;
110 var tagNext = dir === 'left' ? tag - 1 : tag + 1;
111 var nodeToGetWidth = dir === 'left' ? tag - 1 : totalLen - index;
112
113 if (dir === 'left' && tag === 0) {
114 ret[0] = 0;
115 return ret;
116 } else if (dir === 'right' && tag === len - 1) {
117 ret[tag] = 0;
118 return ret;
119 }
120
121 // header with no dataSource
122 var isEmpty = !(dataSource && dataSource.length > 0);
123 // no header
124 var node = isEmpty ? _this.getHeaderCellNode(0, nodeToGetWidth) : _this.getCellNode(scrollToRow || dataSource[0] && dataSource[0].__rowIndex || 0, nodeToGetWidth);
125 var colWidth = node && parseFloat(getComputedStyle(node).width) || 0;
126
127 ret[tag] = (ret[tagNext] || 0) + colWidth;
128 return ret;
129 }, offsetArr);
130
131 return offsetArr;
132 };
133
134 _this.getTableInstance = function (type, instance) {
135 type = '';
136 _this['table' + type + 'Inc'] = instance;
137 };
138
139 _this.getNode = function (type, node) {
140 _this[type + 'Node'] = node;
141 };
142
143 _this.getFlatenChildren = function () {
144 var children = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
145
146 var loop = function loop(arr) {
147 var newArray = [];
148 arr.forEach(function (child) {
149 if (child.children) {
150 newArray.push.apply(newArray, loop(child.children));
151 } else {
152 newArray.push(child);
153 }
154 });
155 return newArray;
156 };
157
158 return loop(children);
159 };
160
161 _this.state = {
162 hasLockLeft: true,
163 hasLockRight: true
164 };
165
166 _this.pingLeft = false;
167 _this.pingRight = false;
168 return _this;
169 }
170
171 LockTable.prototype.getChildContext = function getChildContext() {
172 return {
173 getTableInstance: this.getTableInstance,
174 getLockNode: this.getNode,
175 onLockBodyScroll: this.onLockBodyScroll
176 };
177 };
178
179 LockTable.prototype.componentDidMount = function componentDidMount() {
180 var dataSource = this.props.dataSource;
181
182 var isEmpty = !(dataSource && dataSource.length > 0);
183
184 this.updateOffsetArr();
185 this.onLockBodyScroll(isEmpty ? { currentTarget: this.headerNode } : { currentTarget: this.bodyNode });
186 this.forceUpdate();
187
188 events.on(window, 'resize', this.updateOffsetArr);
189 };
190
191 LockTable.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState, nextContext) {
192 if (nextProps.pure) {
193 var isEqual = shallowElementEquals(nextProps, this.props);
194 return !(isEqual && obj.shallowEqual(nextContext, this.context));
195 }
196
197 return true;
198 };
199
200 LockTable.prototype.componentDidUpdate = function componentDidUpdate() {
201 this.updateOffsetArr();
202 this.onLockBodyScroll(this.bodyNode ? { currentTarget: this.bodyNode } : { currentTarget: this.headerNode }, true);
203 };
204
205 LockTable.prototype.componentWillUnmount = function componentWillUnmount() {
206 this.pingLeft = false;
207 this.pingRight = false;
208 events.off(window, 'resize', this.updateOffsetArr);
209 };
210
211 LockTable.prototype.normalizeChildrenState = function normalizeChildrenState(props) {
212 var columns = this.normalizeChildren(props);
213
214 this.splitChildren = this.splitFromNormalizeChildren(columns);
215
216 return this.mergeFromSplitLockChildren(this.splitChildren, props.prefix);
217 };
218
219 // 将React结构化数据提取props转换成数组
220
221
222 LockTable.prototype.normalizeChildren = function normalizeChildren(props) {
223 var children = props.children,
224 columns = props.columns;
225
226 var isLock = false,
227 ret = void 0;
228 var getChildren = function getChildren(children) {
229 var ret = [];
230 Children.forEach(children, function (child) {
231 if (child) {
232 var _props = _extends({}, child.props);
233 if ([true, 'left', 'right'].indexOf(_props.lock) > -1) {
234 isLock = true;
235 if (!('width' in _props)) {
236 log.warning('Should config width for lock column named [ ' + _props.dataIndex + ' ].');
237 }
238 }
239 ret.push(_props);
240 if (child.props.children) {
241 _props.children = getChildren(child.props.children);
242 }
243 }
244 });
245 return ret;
246 };
247
248 if (columns && !children) {
249 ret = columns;
250 isLock = columns.find(function (record) {
251 return [true, 'left', 'right'].indexOf(record.lock) > -1;
252 });
253 } else {
254 ret = getChildren(children);
255 }
256 ret.forEach(function (child) {
257 // 为自定义的列特殊处理
258 if (child.__normalized && isLock) {
259 // users can set lock type for column selection now, so its origin lock type comes first
260 child.lock = child.lock || 'left';
261 delete child.__normalized;
262 }
263 });
264 return ret;
265 };
266
267 /**
268 * 从数组中分离出lock的列和正常的列
269 * @param {Array} children
270 * @return {Object} { lockLeftChildren, lockRightChildren, originChildren } 锁左列, 锁左列, 剩余列
271 */
272
273
274 LockTable.prototype.splitFromNormalizeChildren = function splitFromNormalizeChildren(children) {
275 var originChildren = deepCopy(children);
276 var lockLeftChildren = deepCopy(children);
277 var lockRightChildren = deepCopy(children);
278 var loop = function loop(lockChildren, condition) {
279 var ret = [];
280 lockChildren.forEach(function (child) {
281 if (child.children) {
282 var res = loop(child.children, condition);
283 if (!res.length) {
284 ret.push(child);
285 }
286 } else {
287 var order = condition(child);
288 if (!order) {
289 ret.push(child);
290 }
291 }
292 });
293 ret.forEach(function (res) {
294 var index = lockChildren.indexOf(res);
295 lockChildren.splice(index, 1);
296 });
297 return lockChildren;
298 };
299 loop(lockLeftChildren, function (child) {
300 if (child.lock === true || child.lock === 'left') {
301 return 'left';
302 }
303 });
304 loop(lockRightChildren, function (child) {
305 if (child.lock === 'right') {
306 return 'right';
307 }
308 });
309 loop(originChildren, function (child) {
310 return child.lock !== true && child.lock !== 'left' && child.lock !== 'right';
311 });
312 return {
313 lockLeftChildren: lockLeftChildren,
314 lockRightChildren: lockRightChildren,
315 originChildren: originChildren
316 };
317 };
318
319 /**
320 * 将左侧的锁列树和中间的普通树及右侧的锁列树进行合并
321 * 会在原始 originChildren 上做改动
322 * @param {Object} splitChildren { lockLeftChildren, lockRightChildren, originChildren }
323 * @return {Array} originChildren
324 */
325
326
327 LockTable.prototype.mergeFromSplitLockChildren = function mergeFromSplitLockChildren(splitChildren, prefix) {
328 var lockLeftChildren = splitChildren.lockLeftChildren,
329 lockRightChildren = splitChildren.lockRightChildren;
330 var originChildren = splitChildren.originChildren;
331
332
333 var flatenLeftChildren = this.getFlatenChildren(lockLeftChildren);
334 var flatenRightChildren = this.getFlatenChildren(lockRightChildren);
335
336 setStickyStyle(lockLeftChildren, flatenLeftChildren, 'left', this.state.leftOffsetArr, prefix);
337 setStickyStyle(lockRightChildren, flatenRightChildren, 'right', this.state.rightOffsetArr, prefix);
338
339 return [].concat(lockLeftChildren, originChildren, lockRightChildren);
340 };
341
342 LockTable.prototype.getCellNode = function getCellNode(index, i) {
343 var table = this.tableInc;
344
345 try {
346 // in case of finding an unmounted component due to cached data
347 // need to clear refs of table when dataSource Changed
348 // use try catch for temporary
349 return findDOMNode(table.getCellRef(index, i));
350 } catch (error) {
351 return null;
352 }
353 };
354
355 LockTable.prototype.getTableNode = function getTableNode() {
356 var table = this.tableInc;
357 try {
358 // in case of finding an unmounted component due to cached data
359 // need to clear refs of table when dataSource Changed
360 // use try catch for temporary
361 return findDOMNode(table.tableEl);
362 } catch (error) {
363 return null;
364 }
365 };
366
367 LockTable.prototype.getHeaderCellNode = function getHeaderCellNode(index, i) {
368 var table = this.tableInc;
369
370 try {
371 // in case of finding an unmounted component due to cached data
372 // need to clear refs of table when dataSource Changed
373 // use try catch for temporary
374 return findDOMNode(table.getHeaderCellRef(index, i));
375 } catch (error) {
376 return null;
377 }
378 };
379
380 LockTable.prototype.render = function render() {
381 var _classnames;
382
383 /* eslint-disable no-unused-vars, prefer-const */
384 var _props2 = this.props,
385 children = _props2.children,
386 columns = _props2.columns,
387 prefix = _props2.prefix,
388 components = _props2.components,
389 scrollToRow = _props2.scrollToRow,
390 className = _props2.className,
391 dataSource = _props2.dataSource,
392 others = _objectWithoutProperties(_props2, ['children', 'columns', 'prefix', 'components', 'scrollToRow', 'className', 'dataSource']);
393
394 var normalizedChildren = this.normalizeChildrenState(this.props);
395
396 components = _extends({}, components);
397 components.Body = components.Body || LockBody;
398 components.Header = components.Header || LockHeader;
399 components.Wrapper = components.Wrapper || LockWrapper;
400 components.Row = components.Row || LockRow;
401 className = classnames((_classnames = {}, _classnames[prefix + 'table-lock'] = true, _classnames[prefix + 'table-stickylock'] = true, _classnames[prefix + 'table-wrap-empty'] = !dataSource.length, _classnames[className] = className, _classnames));
402
403 return React.createElement(BaseComponent, _extends({}, others, {
404 dataSource: dataSource,
405 columns: normalizedChildren,
406 prefix: prefix,
407 components: components,
408 className: className
409 }));
410 };
411
412 return LockTable;
413 }(React.Component), _class.LockRow = LockRow, _class.LockBody = LockBody, _class.LockHeader = LockHeader, _class.propTypes = _extends({
414 scrollToCol: PropTypes.number,
415 /**
416 * 指定滚动到某一行,仅在`useVirtual`的时候生效
417 */
418 scrollToRow: PropTypes.number
419 }, BaseComponent.propTypes), _class.defaultProps = _extends({}, BaseComponent.defaultProps), _class.childContextTypes = {
420 getTableInstance: PropTypes.func,
421 getLockNode: PropTypes.func,
422 onLockBodyScroll: PropTypes.func
423 }, _temp);
424 LockTable.displayName = 'LockTable';
425
426 statics(LockTable, BaseComponent);
427 return LockTable;
428}
429
430function deepCopy(arr) {
431 var copy = function copy(arr) {
432 return arr.map(function (item) {
433 var newItem = _extends({}, item);
434 if (item.children) {
435 item.children = copy(item.children);
436 }
437 return newItem;
438 });
439 };
440 return copy(arr);
441}
\No newline at end of file