antd-table-infinity
Advanced tools
Comparing version 1.1.3 to 1.1.5
@@ -440,3 +440,4 @@ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
shouldUpdate = [1, maxPage, maxPage - 1, maxPage - 2].includes(currentPage) ? dataSource.length > 0 : [// 如果已经加载3页数据,需要更新视图,否则不在 fullLoading 状态,即使数据不满3页也需要更新视图 | ||
...(this.fullLoading ? [] : [0, pageSize, pageSize * 2]), pageSize * 3].includes(dataSource.length); | ||
// 兼容部份项目,每页,可能多一个条目的情况,如一系列数据,有个合计行 | ||
...(this.fullLoading ? [] : [0, pageSize, pageSize + 1, pageSize * 2, pageSize * 2 + 1, pageSize * 2 + 2]), pageSize * 3, pageSize * 3 + 1, pageSize * 3 + 2, pageSize * 3 + 3].includes(dataSource.length); | ||
debug && console.log('shouldUpdate:3Page', { | ||
@@ -443,0 +444,0 @@ currentPage, |
@@ -455,3 +455,4 @@ "use strict"; | ||
shouldUpdate = [1, maxPage, maxPage - 1, maxPage - 2].includes(currentPage) ? dataSource.length > 0 : [// 如果已经加载3页数据,需要更新视图,否则不在 fullLoading 状态,即使数据不满3页也需要更新视图 | ||
...(this.fullLoading ? [] : [0, pageSize, pageSize * 2]), pageSize * 3].includes(dataSource.length); | ||
// 兼容部份项目,每页,可能多一个条目的情况,如一系列数据,有个合计行 | ||
...(this.fullLoading ? [] : [0, pageSize, pageSize + 1, pageSize * 2, pageSize * 2 + 1, pageSize * 2 + 2]), pageSize * 3, pageSize * 3 + 1, pageSize * 3 + 2, pageSize * 3 + 3].includes(dataSource.length); | ||
debug && console.log('shouldUpdate:3Page', { | ||
@@ -458,0 +459,0 @@ currentPage, |
{ | ||
"name": "antd-table-infinity", | ||
"version": "1.1.3", | ||
"version": "1.1.5", | ||
"scripts": { | ||
@@ -80,3 +80,2 @@ "storybook": "start-storybook -p 9001 -c .storybook", | ||
"dependencies": { | ||
"antd-table-infinity": "^1.1.2", | ||
"intersection-observer": "^0.5.1", | ||
@@ -83,0 +82,0 @@ "lodash.throttle": "^4.1.1" |
@@ -10,98 +10,98 @@ import React, { Component, Fragment } from 'react' | ||
const fetchDebounce = (() => { | ||
const loading = [] | ||
return ({ page, pageSize, onFetch }) => { | ||
const now = Date.now() | ||
if (!loading[page] || (loading[page] && now - loading[page] > 1000)) { | ||
onFetch({ page, pageSize }) | ||
} | ||
loading[page] = now | ||
} | ||
const loading = [] | ||
return ({ page, pageSize, onFetch }) => { | ||
const now = Date.now() | ||
if (!loading[page] || (loading[page] && now - loading[page] > 1000)) { | ||
onFetch({ page, pageSize }) | ||
} | ||
loading[page] = now | ||
} | ||
})() | ||
function getCachePage({ step, maxPage, pageSize, onFetch, loading, cacheData, currentPage }) { | ||
const pageBefore = currentPage - step | ||
const pageAfter = currentPage + step | ||
const cachePageDataBefore = cacheData[pageBefore] | ||
const cachePageData = cacheData[currentPage] | ||
const cachePageDataAfter = cacheData[pageAfter] | ||
if (!loading) { | ||
!cachePageData && | ||
currentPage > 0 && | ||
currentPage <= maxPage && | ||
fetchDebounce({ page: currentPage, pageSize, onFetch }) | ||
!cachePageDataBefore && | ||
pageBefore > 0 && | ||
pageBefore <= maxPage && | ||
fetchDebounce({ page: pageBefore, pageSize, onFetch }) | ||
!cachePageDataAfter && | ||
pageAfter > 0 && | ||
pageAfter <= maxPage && | ||
fetchDebounce({ page: pageAfter, pageSize, onFetch }) | ||
const pageBefore = currentPage - step | ||
const pageAfter = currentPage + step | ||
const cachePageDataBefore = cacheData[pageBefore] | ||
const cachePageData = cacheData[currentPage] | ||
const cachePageDataAfter = cacheData[pageAfter] | ||
if (!loading) { | ||
!cachePageData && | ||
currentPage > 0 && | ||
currentPage <= maxPage && | ||
fetchDebounce({ page: currentPage, pageSize, onFetch }) | ||
!cachePageDataBefore && | ||
pageBefore > 0 && | ||
pageBefore <= maxPage && | ||
fetchDebounce({ page: pageBefore, pageSize, onFetch }) | ||
!cachePageDataAfter && | ||
pageAfter > 0 && | ||
pageAfter <= maxPage && | ||
fetchDebounce({ page: pageAfter, pageSize, onFetch }) | ||
} | ||
let lastOwnDataPage = currentPage | ||
if (cachePageDataAfter) { | ||
lastOwnDataPage = pageAfter | ||
} else if (cachePageData) { | ||
lastOwnDataPage = currentPage | ||
} else if (cachePageDataBefore) { | ||
lastOwnDataPage = pageBefore | ||
} | ||
let lastOwnDataPage = currentPage | ||
if (cachePageDataAfter) { | ||
lastOwnDataPage = pageAfter | ||
} else if (cachePageData) { | ||
lastOwnDataPage = currentPage | ||
} else if (cachePageDataBefore) { | ||
lastOwnDataPage = pageBefore | ||
} | ||
return [cachePageDataBefore || [], cachePageData || [], cachePageDataAfter || [], lastOwnDataPage] | ||
return [cachePageDataBefore || [], cachePageData || [], cachePageDataAfter || [], lastOwnDataPage] | ||
} | ||
const computeState = ( | ||
{ pageSize, onFetch, loading, total, bidirectionalCachePages: cachePages, dataSource: [page, data] }, | ||
{ maxPage, currentPage, cacheData, rowHeight }, | ||
{ pageSize, onFetch, loading, total, bidirectionalCachePages: cachePages, dataSource: [page, data] }, | ||
{ maxPage, currentPage, cacheData, rowHeight }, | ||
) => { | ||
let bidirectionalCachePages = cachePages | ||
let newCacheData = cacheData | ||
let bidirectionalCachePages = cachePages | ||
let newCacheData = cacheData | ||
if (cachePages < 1) { | ||
bidirectionalCachePages = 1 | ||
} | ||
if (bidirectionalCachePages * 2 + 1 < maxPage) { | ||
let startPage = currentPage - bidirectionalCachePages | ||
startPage = startPage < 1 ? 1 : startPage | ||
if (cachePages < 1) { | ||
bidirectionalCachePages = 1 | ||
} | ||
if (bidirectionalCachePages * 2 + 1 < maxPage) { | ||
let startPage = currentPage - bidirectionalCachePages | ||
startPage = startPage < 1 ? 1 : startPage | ||
let endPage = currentPage + bidirectionalCachePages | ||
endPage = endPage > maxPage ? maxPage : endPage | ||
let endPage = currentPage + bidirectionalCachePages | ||
endPage = endPage > maxPage ? maxPage : endPage | ||
cacheData[page] = data // eslint-disable-line | ||
newCacheData = Array.from({ length: maxPage }) | ||
cacheData[page] = data // eslint-disable-line | ||
newCacheData = Array.from({ length: maxPage }) | ||
Array.prototype.splice.apply( | ||
newCacheData, | ||
[startPage, endPage - startPage + 1].concat(cacheData.slice(startPage, endPage + 1)), | ||
) | ||
} | ||
newCacheData[page] = data | ||
Array.prototype.splice.apply( | ||
newCacheData, | ||
[startPage, endPage - startPage + 1].concat(cacheData.slice(startPage, endPage + 1)), | ||
) | ||
} | ||
newCacheData[page] = data | ||
const [cachePageDataBefore, cachePageData, cachePageDataAfter, lastOwnDataPage] = getCachePage({ | ||
step: 1, | ||
maxPage, | ||
pageSize, | ||
onFetch, | ||
loading, | ||
cacheData: newCacheData, | ||
currentPage, | ||
}) | ||
const [cachePageDataBefore, cachePageData, cachePageDataAfter, lastOwnDataPage] = getCachePage({ | ||
step: 1, | ||
maxPage, | ||
pageSize, | ||
onFetch, | ||
loading, | ||
cacheData: newCacheData, | ||
currentPage, | ||
}) | ||
const upperHeight = Math.round((currentPage - 1 - (cachePageDataBefore.length ? 1 : 0)) * pageSize * rowHeight) | ||
const upperHeight = Math.round((currentPage - 1 - (cachePageDataBefore.length ? 1 : 0)) * pageSize * rowHeight) | ||
const underHeight = Math.round( | ||
// 已经是最后一页,直接返回0,否则如果有零头total % pageSize,需要加上不满一个page的零头lastOwnDataPageLength,但需要少加一个整页pageSize | ||
maxPage === lastOwnDataPage ? 0 : ((maxPage - lastOwnDataPage) * pageSize + (total % pageSize)) * rowHeight, | ||
) | ||
const dataSource = cachePageDataBefore.concat(cachePageData).concat(cachePageDataAfter) | ||
const underHeight = Math.round( | ||
// 已经是最后一页,直接返回0,否则如果有零头total % pageSize,需要加上不满一个page的零头lastOwnDataPageLength,但需要少加一个整页pageSize | ||
maxPage === lastOwnDataPage ? 0 : ((maxPage - lastOwnDataPage) * pageSize + (total % pageSize)) * rowHeight, | ||
) | ||
const dataSource = cachePageDataBefore.concat(cachePageData).concat(cachePageDataAfter) | ||
return { | ||
hasCacheBefore: !!cachePageDataBefore.length, | ||
hasCache: !!cachePageData.length, | ||
hasCacheAfter: !!cachePageDataAfter.length, | ||
cacheData: newCacheData, | ||
dataSource, | ||
underHeight, | ||
upperHeight, | ||
} | ||
return { | ||
hasCacheBefore: !!cachePageDataBefore.length, | ||
hasCache: !!cachePageData.length, | ||
hasCacheAfter: !!cachePageDataAfter.length, | ||
cacheData: newCacheData, | ||
dataSource, | ||
underHeight, | ||
upperHeight, | ||
} | ||
} | ||
@@ -111,482 +111,466 @@ | ||
class InfinityTable extends Component { | ||
static PlaceHolder({ height, domNode, loading, loadingIndicator }) { | ||
return ( | ||
domNode && | ||
ReactDOM.createPortal(<div style={{ height: `${height}px` }}>{loading && loadingIndicator} </div>, domNode) | ||
) | ||
} | ||
static LoadingPlaceHolder({ domNode, loading, loadingIndicator }) { | ||
return domNode && ReactDOM.createPortal(loading ? loadingIndicator : <div />, domNode) | ||
} | ||
state = { | ||
scrollTop: 0, // 滚动条位置 | ||
dataSource: [], // DOM上显示的数据 | ||
rowHeight: 0, // 每行的实际高度 | ||
cacheData: [], // 缓存的数据集大小 | ||
currentPage: 1, // 当前页位置 | ||
underHeight: 0, // 下撑高 | ||
upperHeight: 0, // 上撑高 | ||
} | ||
static getDerivedStateFromProps(props, prevState) { | ||
return computeState(props, prevState) | ||
} | ||
static PlaceHolder({ height, domNode, loading, loadingIndicator }) { | ||
return ( | ||
domNode && | ||
ReactDOM.createPortal(<div style={{ height: `${height}px` }}>{loading && loadingIndicator} </div>, domNode) | ||
) | ||
} | ||
static LoadingPlaceHolder({ domNode, loading, loadingIndicator }) { | ||
return domNode && ReactDOM.createPortal(loading ? loadingIndicator : <div />, domNode) | ||
} | ||
state = { | ||
scrollTop: 0, // 滚动条位置 | ||
dataSource: [], // DOM上显示的数据 | ||
rowHeight: 0, // 每行的实际高度 | ||
cacheData: [], // 缓存的数据集大小 | ||
currentPage: 1, // 当前页位置 | ||
underHeight: 0, // 下撑高 | ||
upperHeight: 0, // 上撑高 | ||
} | ||
static getDerivedStateFromProps(props, prevState) { | ||
return computeState(props, prevState) | ||
} | ||
componentDidMount() { | ||
/* eslint-disable */ | ||
this.refRoot = ReactDOM.findDOMNode(this) | ||
this.refScroll = this.refRoot.getElementsByClassName('ant-table-body')[0] | ||
this.refTable = this.refScroll.getElementsByTagName('tbody')[0] | ||
/* eslint-enabled */ | ||
this.createUnderPlaceholder() | ||
this.createUpperPlaceholder() | ||
this.setStateWithThrottle = throttle(this.updateTable, 0) | ||
this.props.onScroll && this.refScroll.addEventListener('scroll', this.props.onScroll) | ||
componentDidMount() { | ||
/* eslint-disable */ | ||
this.refRoot = ReactDOM.findDOMNode(this) | ||
this.refScroll = this.refRoot.getElementsByClassName('ant-table-body')[0] | ||
this.refTable = this.refScroll.getElementsByTagName('tbody')[0] | ||
/* eslint-enabled */ | ||
this.createUnderPlaceholder() | ||
this.createUpperPlaceholder() | ||
this.setStateWithThrottle = throttle(this.updateTable, 0) | ||
this.props.onScroll && this.refScroll.addEventListener('scroll', this.props.onScroll) | ||
if (this.refScroll) { | ||
this.io = new IntersectionObserver( | ||
changes => { | ||
const { debug } = this.props | ||
let shouldUpdate = true | ||
this.ioTargetState = changes.reduce((result, change) => { | ||
const ret = { ...result } | ||
switch (change.target) { | ||
case this.refPageBoundaryBefore: | ||
ret.refPageBoundaryBefore = change | ||
// Bug fix: | ||
// 向下滚动的时候,如果边界第一行数据触发,不需要更新视图,因为上一页的最后一行已经触发了视图更新, | ||
// 但是用户通过点击分页切换,而不是滚动的情况例外,因为此时需要更新页码 | ||
if ( | ||
!this.isPaginationClick && | ||
changes.length === 1 && | ||
this.state.direction === 'down' | ||
) { | ||
shouldUpdate = false | ||
} | ||
if (this.refScroll) { | ||
this.io = new IntersectionObserver( | ||
changes => { | ||
const { debug } = this.props | ||
let shouldUpdate = true | ||
this.ioTargetState = changes.reduce((result, change) => { | ||
const ret = { ...result } | ||
switch (change.target) { | ||
case this.refPageBoundaryBefore: | ||
ret.refPageBoundaryBefore = change | ||
// Bug fix: | ||
// 向下滚动的时候,如果边界第一行数据触发,不需要更新视图,因为上一页的最后一行已经触发了视图更新, | ||
// 但是用户通过点击分页切换,而不是滚动的情况例外,因为此时需要更新页码 | ||
if (!this.isPaginationClick && changes.length === 1 && this.state.direction === 'down') { | ||
shouldUpdate = false | ||
} | ||
break | ||
case this.refPageBoundaryAfter: | ||
ret.refPageBoundaryAfter = change | ||
// Bug fix: | ||
// 向上滚动的时候,如果边界最后行数据触发,不需要更新视图,因为上一页的第一行已经触发了视图更新 | ||
// 但是用户通过点击分页切换,而不是滚动的情况例外,因为此时需要更新页码 | ||
if (!this.isPaginationClick && changes.length === 1 && this.state.direction === 'up') { | ||
shouldUpdate = false | ||
} | ||
break | ||
default: // console.log(change) | ||
} | ||
return ret | ||
}, this.ioTargetState) | ||
break | ||
case this.refPageBoundaryAfter: | ||
ret.refPageBoundaryAfter = change | ||
// Bug fix: | ||
// 向上滚动的时候,如果边界最后行数据触发,不需要更新视图,因为上一页的第一行已经触发了视图更新 | ||
// 但是用户通过点击分页切换,而不是滚动的情况例外,因为此时需要更新页码 | ||
if (!this.isPaginationClick && changes.length === 1 && this.state.direction === 'up') { | ||
shouldUpdate = false | ||
} | ||
break | ||
default: // console.log(change) | ||
} | ||
return ret | ||
}, this.ioTargetState) | ||
this.isPaginationClick = false | ||
this.isPaginationClick = false | ||
debug && | ||
console.log('shouldUpdate:IntersectionObserver', { | ||
shouldUpdate, | ||
changes, | ||
}) | ||
debug && | ||
console.log('shouldUpdate:IntersectionObserver', { | ||
shouldUpdate, | ||
changes, | ||
}) | ||
shouldUpdate && this.setStateWithThrottle() | ||
}, | ||
{ | ||
root: this.refScroll, | ||
threshold: [0, 1], | ||
}, | ||
) | ||
this.toggleObserver() | ||
} | ||
shouldUpdate && this.setStateWithThrottle() | ||
}, | ||
{ | ||
root: this.refScroll, | ||
threshold: [0, 1], | ||
}, | ||
) | ||
this.toggleObserver() | ||
} | ||
this.props.onFetch({ page: 1, pageSize: this.props.pageSize }) | ||
this.props.onFetch({ page: 1, pageSize: this.props.pageSize }) | ||
new Promise(reslove => { | ||
this.initialReslove = reslove | ||
}).then(() => { | ||
const { | ||
pageSize, | ||
pagination: { defaultCurrent = 1 }, | ||
} = this.props | ||
this.createLoadingPlaceholder() // fullLoading | ||
this.scrollToPage(defaultCurrent) | ||
const visibleRowCount = Math.round(this.refScroll.clientHeight / this.state.rowHeight) | ||
this.isReady = true | ||
if (pageSize < visibleRowCount) { | ||
console.warn( | ||
`pagesize(${pageSize}) less than visible row count(${visibleRowCount}), maybe you set error!`, | ||
) | ||
} | ||
}) | ||
} | ||
shouldComponentUpdate(nextProps, nextState) { | ||
const { pageSize, debug } = nextProps | ||
const { maxPage, currentPage, dataSource, direction, rowHeight } = nextState | ||
let shouldUpdate = true | ||
new Promise(reslove => { | ||
this.initialReslove = reslove | ||
}).then(() => { | ||
const { | ||
pageSize, | ||
pagination: { defaultCurrent = 1 }, | ||
} = this.props | ||
this.createLoadingPlaceholder() // fullLoading | ||
this.scrollToPage(defaultCurrent) | ||
const visibleRowCount = Math.round(this.refScroll.clientHeight / this.state.rowHeight) | ||
this.isReady = true | ||
if (pageSize < visibleRowCount) { | ||
console.warn(`pagesize(${pageSize}) less than visible row count(${visibleRowCount}), maybe you set error!`) | ||
} | ||
}) | ||
} | ||
shouldComponentUpdate(nextProps, nextState) { | ||
const { pageSize, debug } = nextProps | ||
const { maxPage, currentPage, dataSource, direction, rowHeight } = nextState | ||
let shouldUpdate = true | ||
// 有且仅有当前页上下边界同时触发时,表示更换边界,不需要更新 | ||
if (this.isPageBoundary) { | ||
shouldUpdate = false | ||
this.isPageBoundary = false | ||
} | ||
debug && console.log('shouldUpdate:updateBoundary', { shouldUpdate }) | ||
// 有且仅有当前页上下边界同时触发时,表示更换边界,不需要更新 | ||
if (this.isPageBoundary) { | ||
shouldUpdate = false | ||
this.isPageBoundary = false | ||
} | ||
debug && console.log('shouldUpdate:updateBoundary', { shouldUpdate }) | ||
if (shouldUpdate && !this.isInitial) { | ||
let currentPageReal = currentPage | ||
if (shouldUpdate && !this.isInitial) { | ||
let currentPageReal = currentPage | ||
// 由于state中的currentPage延迟与真实的currentPage不一致,当出现这种情况时,强制updateTable() | ||
currentPageReal = Math.floor(this.refScroll.scrollTop / (pageSize * rowHeight)) + 1 || 1 | ||
currentPageReal = currentPageReal > maxPage ? maxPage : currentPageReal | ||
if (currentPageReal !== currentPage) { | ||
this.updateTable() | ||
shouldUpdate = false | ||
} | ||
// 由于state中的currentPage延迟与真实的currentPage不一致,当出现这种情况时,强制updateTable() | ||
currentPageReal = Math.floor(this.refScroll.scrollTop / (pageSize * rowHeight)) + 1 || 1 | ||
currentPageReal = currentPageReal > maxPage ? maxPage : currentPageReal | ||
if (currentPageReal !== currentPage) { | ||
this.updateTable() | ||
shouldUpdate = false | ||
} | ||
debug && | ||
console.log('shouldUpdate:currentPageReal', { | ||
currentPageReal, | ||
currentPage, | ||
shouldUpdate, | ||
}) | ||
} | ||
debug && | ||
console.log('shouldUpdate:currentPageReal', { | ||
currentPageReal, | ||
currentPage, | ||
shouldUpdate, | ||
}) | ||
} | ||
if (shouldUpdate) { | ||
const { refPageBoundaryBefore, refPageBoundaryAfter } = this.ioTargetState | ||
if (shouldUpdate) { | ||
const { refPageBoundaryBefore, refPageBoundaryAfter } = this.ioTargetState | ||
switch (direction) { | ||
case 'up': | ||
if ( | ||
this.state.hasCacheAfter && | ||
currentPage < maxPage - 2 && // Bug fix: 如果是最后三页向后点击切换,需要刷新视图,否则页码表记停留在前一页 | ||
refPageBoundaryBefore && | ||
refPageBoundaryBefore.intersectionRatio > 0 | ||
) | ||
shouldUpdate = false | ||
break | ||
switch (direction) { | ||
case 'up': | ||
if ( | ||
this.state.hasCacheAfter && | ||
currentPage < maxPage - 2 && // Bug fix: 如果是最后三页向后点击切换,需要刷新视图,否则页码表记停留在前一页 | ||
refPageBoundaryBefore && | ||
refPageBoundaryBefore.intersectionRatio > 0 | ||
) | ||
shouldUpdate = false | ||
break | ||
case 'down': | ||
if ( | ||
this.state.currentPage === currentPage && | ||
this.state.hasCacheAfter && // Bug fix: 可能存在3页数据已经加载到了, 但之前没有刷新的情况,所以需要刷新,判断特征是之前的数据小于3页 | ||
refPageBoundaryAfter && | ||
refPageBoundaryAfter.intersectionRatio > 0 | ||
) | ||
shouldUpdate = false | ||
} | ||
case 'down': | ||
if ( | ||
this.state.currentPage === currentPage && | ||
this.state.hasCacheAfter && // Bug fix: 可能存在3页数据已经加载到了, 但之前没有刷新的情况,所以需要刷新,判断特征是之前的数据小于3页 | ||
refPageBoundaryAfter && | ||
refPageBoundaryAfter.intersectionRatio > 0 | ||
) | ||
shouldUpdate = false | ||
} | ||
debug && | ||
console.log('shouldUpdate:verboseBoundary', { | ||
shouldUpdate, | ||
direction, | ||
dataSource, | ||
Before: refPageBoundaryBefore && refPageBoundaryBefore.intersectionRatio, | ||
After: refPageBoundaryAfter && refPageBoundaryAfter.intersectionRatio, | ||
}) | ||
} | ||
debug && | ||
console.log('shouldUpdate:verboseBoundary', { | ||
shouldUpdate, | ||
direction, | ||
dataSource, | ||
Before: refPageBoundaryBefore && refPageBoundaryBefore.intersectionRatio, | ||
After: refPageBoundaryAfter && refPageBoundaryAfter.intersectionRatio, | ||
}) | ||
} | ||
if (shouldUpdate) { | ||
shouldUpdate = [1, maxPage, maxPage - 1, maxPage - 2].includes(currentPage) | ||
? dataSource.length > 0 | ||
: [ | ||
// 如果已经加载3页数据,需要更新视图,否则不在 fullLoading 状态,即使数据不满3页也需要更新视图 | ||
...(this.fullLoading ? [] : [0, pageSize, pageSize * 2]), | ||
pageSize * 3, | ||
].includes(dataSource.length) | ||
if (shouldUpdate) { | ||
shouldUpdate = [1, maxPage, maxPage - 1, maxPage - 2].includes(currentPage) | ||
? dataSource.length > 0 | ||
: [ | ||
// 如果已经加载3页数据,需要更新视图,否则不在 fullLoading 状态,即使数据不满3页也需要更新视图 | ||
// 兼容部份项目,每页,可能多一个条目的情况,如一系列数据,有个合计行 | ||
...(this.fullLoading ? [] : [0, pageSize, pageSize + 1, pageSize * 2, pageSize * 2 + 1, pageSize * 2 + 2]), | ||
pageSize * 3, | ||
pageSize * 3 + 1, | ||
pageSize * 3 + 2, | ||
pageSize * 3 + 3, | ||
].includes(dataSource.length) | ||
debug && | ||
console.log('shouldUpdate:3Page', { | ||
currentPage, | ||
dataSource, | ||
shouldUpdate, | ||
state: this.state, | ||
nextState, | ||
}) | ||
} | ||
debug && | ||
console.log('shouldUpdate:3Page', { | ||
currentPage, | ||
dataSource, | ||
shouldUpdate, | ||
state: this.state, | ||
nextState, | ||
}) | ||
} | ||
return shouldUpdate | ||
} | ||
componentDidUpdate(preProps, preState) { | ||
const { hasCache, hasCacheBefore, currentPage, dataSource, maxPage } = this.state | ||
const { pageSize, debug } = this.props | ||
return shouldUpdate | ||
} | ||
componentDidUpdate(preProps, preState) { | ||
const { hasCache, hasCacheBefore, currentPage, dataSource, maxPage } = this.state | ||
const { pageSize, debug } = this.props | ||
if (!this.isInitial) { | ||
let before, after | ||
if (!this.isInitial) { | ||
let before, after | ||
if (hasCache && hasCacheBefore) { | ||
if (currentPage === 1 || currentPage === maxPage - 1) { | ||
before = 0 | ||
after = pageSize - 1 | ||
} else if (currentPage === maxPage) { | ||
before = 0 | ||
after = dataSource.length - 1 | ||
} else { | ||
before = pageSize | ||
after = pageSize * 2 - 1 | ||
} | ||
if (hasCache && hasCacheBefore) { | ||
if (currentPage === 1 || currentPage === maxPage - 1) { | ||
before = 0 | ||
after = pageSize - 1 | ||
} else if (currentPage === maxPage) { | ||
before = 0 | ||
after = dataSource.length - 1 | ||
} else { | ||
before = pageSize | ||
after = pageSize * 2 - 1 | ||
} | ||
if (this.refPageBoundaryBefore !== this.refTable.children[before]) { | ||
this.refPageBoundaryBefore && this.io.unobserve(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.unobserve(this.refPageBoundaryAfter) | ||
if (this.refPageBoundaryBefore !== this.refTable.children[before]) { | ||
this.refPageBoundaryBefore && this.io.unobserve(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.unobserve(this.refPageBoundaryAfter) | ||
this.refPageBoundaryBefore = this.refTable.children[before] | ||
this.refPageBoundaryAfter = this.refTable.children[after] | ||
this.refPageBoundaryBefore = this.refTable.children[before] | ||
this.refPageBoundaryAfter = this.refTable.children[after] | ||
this.refPageBoundaryAfter && this.io.observe(this.refPageBoundaryAfter) | ||
this.refPageBoundaryBefore && this.io.observe(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.observe(this.refPageBoundaryAfter) | ||
this.refPageBoundaryBefore && this.io.observe(this.refPageBoundaryBefore) | ||
debug && | ||
this.refPageBoundaryBefore && | ||
console.log('componentDidUpdate', this.refPageBoundaryBefore.innerText) | ||
debug && | ||
this.refPageBoundaryAfter && | ||
console.log('componentDidUpdate', this.refPageBoundaryAfter.innerText) | ||
debug && this.refPageBoundaryBefore && console.log('componentDidUpdate', this.refPageBoundaryBefore.innerText) | ||
debug && this.refPageBoundaryAfter && console.log('componentDidUpdate', this.refPageBoundaryAfter.innerText) | ||
this.isPageBoundary = true | ||
} | ||
} | ||
const emptyPlaceholder = this.refRoot.querySelector('.ant-table-placeholder') | ||
emptyPlaceholder && (emptyPlaceholder.style.display = 'none') | ||
} | ||
} | ||
componentWillUnmount() { | ||
this.props.onScroll && this.refScroll.removeEventListener('scroll', this.props.onScroll) | ||
this.io.disconnect() | ||
} | ||
isInitial = true // 初始状态,未渲染任何数据 | ||
isReady = false // 已渲染首屏数据,可交互的状态 | ||
isPageBoundary = false // 当前页,上下边界更新 | ||
isPaginationClick = false | ||
refUpperPlaceholder = null | ||
refUnderPlaceholder = null | ||
refLoadingPlaceholder = null | ||
refScroll = null | ||
refTable = null | ||
refFooter = null | ||
refPageBoundaryBefore = null // 当前页的第一条DOM数据 | ||
refPageBoundaryAfter = null // 当前页的最后一条DOM数据 | ||
ioTargetState = { | ||
refUnderPlaceholder: null, | ||
refUpperPlaceholder: null, | ||
refTable: null, | ||
refPageBoundaryBefore: null, | ||
refPageBoundaryAfter: null, | ||
} | ||
createLoadingPlaceholder() { | ||
const refLoadingPlaceholder = document.createElement('div') | ||
refLoadingPlaceholder.setAttribute('id', 'refLoadingPlaceholder') | ||
const clientRect = this.refScroll.getBoundingClientRect() | ||
refLoadingPlaceholder.setAttribute( | ||
'style', | ||
`position:fixed; width:${clientRect.width}px; height:${clientRect.height}px`, | ||
) | ||
this.isPageBoundary = true | ||
} | ||
} | ||
const emptyPlaceholder = this.refRoot.querySelector('.ant-table-placeholder') | ||
emptyPlaceholder && (emptyPlaceholder.style.display = 'none') | ||
} | ||
} | ||
componentWillUnmount() { | ||
this.props.onScroll && this.refScroll.removeEventListener('scroll', this.props.onScroll) | ||
this.io.disconnect() | ||
} | ||
isInitial = true // 初始状态,未渲染任何数据 | ||
isReady = false // 已渲染首屏数据,可交互的状态 | ||
isPageBoundary = false // 当前页,上下边界更新 | ||
isPaginationClick = false | ||
refUpperPlaceholder = null | ||
refUnderPlaceholder = null | ||
refLoadingPlaceholder = null | ||
refScroll = null | ||
refTable = null | ||
refFooter = null | ||
refPageBoundaryBefore = null // 当前页的第一条DOM数据 | ||
refPageBoundaryAfter = null // 当前页的最后一条DOM数据 | ||
ioTargetState = { | ||
refUnderPlaceholder: null, | ||
refUpperPlaceholder: null, | ||
refTable: null, | ||
refPageBoundaryBefore: null, | ||
refPageBoundaryAfter: null, | ||
} | ||
createLoadingPlaceholder() { | ||
const refLoadingPlaceholder = document.createElement('div') | ||
refLoadingPlaceholder.setAttribute('id', 'refLoadingPlaceholder') | ||
const clientRect = this.refScroll.getBoundingClientRect() | ||
refLoadingPlaceholder.setAttribute( | ||
'style', | ||
`position:fixed; width:${clientRect.width}px; height:${clientRect.height}px`, | ||
) | ||
this.refScroll.insertBefore(refLoadingPlaceholder, this.refScroll.firstChild) | ||
this.refLoadingPlaceholder = refLoadingPlaceholder | ||
} | ||
this.refScroll.insertBefore(refLoadingPlaceholder, this.refScroll.firstChild) | ||
this.refLoadingPlaceholder = refLoadingPlaceholder | ||
} | ||
// 创建底部填充块 | ||
createUnderPlaceholder() { | ||
const refUnderPlaceholder = document.createElement('div') | ||
refUnderPlaceholder.setAttribute('id', 'refUnderPlaceholder') | ||
this.refScroll.appendChild(refUnderPlaceholder) | ||
this.refUnderPlaceholder = refUnderPlaceholder | ||
} | ||
// 创建顶部填充块 | ||
createUpperPlaceholder() { | ||
const refUpperPlaceholder = document.createElement('div') | ||
refUpperPlaceholder.setAttribute('id', 'refUpperPlaceholder') | ||
this.refScroll.insertBefore(refUpperPlaceholder, this.refScroll.firstChild) | ||
this.refUpperPlaceholder = refUpperPlaceholder | ||
} | ||
// 创建底部填充块 | ||
createUnderPlaceholder() { | ||
const refUnderPlaceholder = document.createElement('div') | ||
refUnderPlaceholder.setAttribute('id', 'refUnderPlaceholder') | ||
this.refScroll.appendChild(refUnderPlaceholder) | ||
this.refUnderPlaceholder = refUnderPlaceholder | ||
} | ||
// 创建顶部填充块 | ||
createUpperPlaceholder() { | ||
const refUpperPlaceholder = document.createElement('div') | ||
refUpperPlaceholder.setAttribute('id', 'refUpperPlaceholder') | ||
this.refScroll.insertBefore(refUpperPlaceholder, this.refScroll.firstChild) | ||
this.refUpperPlaceholder = refUpperPlaceholder | ||
} | ||
scrollToPage = (...args) => { | ||
const { pageSize } = this.props | ||
const { rowHeight } = this.state | ||
const [page] = args | ||
this.refScroll.scrollTop = (page - 1) * pageSize * rowHeight + 1 // Bug fix: + 1个像素,点击页码向后翻页时存在少一个像素就变成上一页的bug | ||
this.isPaginationClick = true | ||
this.props.pagination && this.props.pagination.onChange && this.props.pagination.onChange(...args) | ||
} | ||
updateTable = () => { | ||
const { clientHeight: tableHeight } = this.refTable | ||
const { scrollTop } = this.refScroll | ||
let { rowHeight, maxPage } = this.state | ||
const { pageSize, total, debug } = this.props | ||
scrollToPage = (...args) => { | ||
const { pageSize } = this.props | ||
const { rowHeight } = this.state | ||
const [page] = args | ||
this.refScroll.scrollTop = (page - 1) * pageSize * rowHeight + 1 // Bug fix: + 1个像素,点击页码向后翻页时存在少一个像素就变成上一页的bug | ||
this.isPaginationClick = true | ||
this.props.pagination && this.props.pagination.onChange && this.props.pagination.onChange(...args) | ||
} | ||
updateTable = () => { | ||
const { clientHeight: tableHeight } = this.refTable | ||
const { scrollTop } = this.refScroll | ||
let { rowHeight, maxPage } = this.state | ||
const { pageSize, total, debug } = this.props | ||
if (this.isInitial) { | ||
// 还在初始状态 | ||
maxPage = Math.ceil(total / pageSize) | ||
const { | ||
dataSource: { length }, | ||
} = this.state | ||
rowHeight = tableHeight / length | ||
if (rowHeight) { | ||
// 当行高出现,表示初次呈现数据数时 | ||
this.isInitial = false | ||
this.initialReslove() | ||
} | ||
} | ||
let currentPage = Math.floor(scrollTop / (pageSize * rowHeight)) + 1 || 1 | ||
currentPage = currentPage > maxPage ? maxPage : currentPage | ||
if (this.isInitial) { | ||
// 还在初始状态 | ||
maxPage = Math.ceil(total / pageSize) | ||
const { | ||
dataSource: { length }, | ||
} = this.state | ||
rowHeight = tableHeight / length | ||
if (rowHeight) { | ||
// 当行高出现,表示初次呈现数据数时 | ||
this.isInitial = false | ||
this.initialReslove() | ||
} | ||
} | ||
let currentPage = Math.floor(scrollTop / (pageSize * rowHeight)) + 1 || 1 | ||
currentPage = currentPage > maxPage ? maxPage : currentPage | ||
debug && | ||
console.log('updateTable', { | ||
currentPage, | ||
}) | ||
this.setState( | ||
{ | ||
direction: this.refScroll.scrollTop - this.state.scrollTop < 0 ? 'up' : 'down', | ||
scrollTop: this.refScroll.scrollTop, | ||
maxPage, | ||
currentPage, | ||
rowHeight, | ||
}, | ||
// () => console.log('computeState done', this.state), | ||
) | ||
} | ||
toggleObserver(condition = true) { | ||
if (condition) { | ||
this.io.observe(this.refUpperPlaceholder) | ||
this.io.observe(this.refUnderPlaceholder) | ||
this.io.observe(this.refTable) | ||
this.refPageBoundaryBefore && this.io.observe(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.observe(this.refPageBoundaryAfter) | ||
} else { | ||
this.io.unobserve(this.refUpperPlaceholder) | ||
this.io.unobserve(this.refUnderPlaceholder) | ||
this.io.unobserve(this.refTable) | ||
this.refPageBoundaryBefore && this.io.unobserve(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.unobserve(this.refPageBoundaryAfter) | ||
} | ||
} | ||
render() { | ||
const { | ||
pageSize, | ||
loadingIndicator, | ||
loading, | ||
total, | ||
columns, | ||
debug, | ||
forwardedRef, | ||
pagination, | ||
...rest | ||
} = this.props | ||
debug && | ||
console.log('updateTable', { | ||
currentPage, | ||
}) | ||
this.setState( | ||
{ | ||
direction: this.refScroll.scrollTop - this.state.scrollTop < 0 ? 'up' : 'down', | ||
scrollTop: this.refScroll.scrollTop, | ||
maxPage, | ||
currentPage, | ||
rowHeight, | ||
}, | ||
// () => console.log('computeState done', this.state), | ||
) | ||
} | ||
toggleObserver(condition = true) { | ||
if (condition) { | ||
this.io.observe(this.refUpperPlaceholder) | ||
this.io.observe(this.refUnderPlaceholder) | ||
this.io.observe(this.refTable) | ||
this.refPageBoundaryBefore && this.io.observe(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.observe(this.refPageBoundaryAfter) | ||
} else { | ||
this.io.unobserve(this.refUpperPlaceholder) | ||
this.io.unobserve(this.refUnderPlaceholder) | ||
this.io.unobserve(this.refTable) | ||
this.refPageBoundaryBefore && this.io.unobserve(this.refPageBoundaryBefore) | ||
this.refPageBoundaryAfter && this.io.unobserve(this.refPageBoundaryAfter) | ||
} | ||
} | ||
render() { | ||
const { pageSize, loadingIndicator, loading, total, columns, debug, forwardedRef, pagination, ...rest } = this.props | ||
const { dataSource, upperHeight, underHeight, cacheData, currentPage } = this.state | ||
const { dataSource, upperHeight, underHeight, cacheData, currentPage } = this.state | ||
this.fullLoading = !cacheData[currentPage] | ||
const { fullLoading } = this | ||
debug && console.log('%c Rendering', 'color:#f00;font-weight:bold') | ||
this.fullLoading = !cacheData[currentPage] | ||
const { fullLoading } = this | ||
debug && console.log('%c Rendering', 'color:#f00;font-weight:bold') | ||
return ( | ||
<Fragment> | ||
<InfinityTable.LoadingPlaceHolder | ||
domNode={this.refLoadingPlaceholder} | ||
loading={fullLoading} | ||
loadingIndicator={<Spin tip="Loading..." />} | ||
/> | ||
<InfinityTable.PlaceHolder | ||
height={upperHeight} | ||
domNode={this.refUpperPlaceholder} | ||
loading={loading && !fullLoading} | ||
loadingIndicator={loadingIndicator} | ||
/> | ||
<InfinityTable.PlaceHolder | ||
height={underHeight} | ||
domNode={this.refUnderPlaceholder} | ||
loading={loading && !fullLoading} | ||
loadingIndicator={loadingIndicator} | ||
/> | ||
{total && pagination && ['top', 'both'].includes(pagination.position) && ( | ||
<Pagination | ||
showQuickJumper | ||
{...pagination} | ||
onChange={this.scrollToPage} | ||
current={currentPage} | ||
pageSize={pageSize} | ||
showSizeChanger={false} | ||
total={total} | ||
className={`infinity-page-table-pagination ${ | ||
pagination && pagination.className ? pagination.className : '' | ||
}`} | ||
/> | ||
)} | ||
<Table | ||
rowKey={record => record.key} | ||
{...rest} | ||
ref={forwardedRef} | ||
columns={columns} | ||
dataSource={dataSource} | ||
pagination={false} | ||
className={`infinity-page-table ${rest.className ? rest.className : ''}`} | ||
/> | ||
{total && pagination && [undefined, 'bottom', 'both'].includes(pagination.position) && ( | ||
<Pagination | ||
showQuickJumper | ||
{...pagination} | ||
onChange={this.scrollToPage} | ||
current={currentPage} | ||
pageSize={pageSize} | ||
showSizeChanger={false} | ||
total={total} | ||
className={`infinity-page-table-pagination ${ | ||
pagination && pagination.className ? pagination.className : '' | ||
}`} | ||
/> | ||
)} | ||
</Fragment> | ||
) | ||
} | ||
return ( | ||
<Fragment> | ||
<InfinityTable.LoadingPlaceHolder | ||
domNode={this.refLoadingPlaceholder} | ||
loading={fullLoading} | ||
loadingIndicator={<Spin tip="Loading..." />} | ||
/> | ||
<InfinityTable.PlaceHolder | ||
height={upperHeight} | ||
domNode={this.refUpperPlaceholder} | ||
loading={loading && !fullLoading} | ||
loadingIndicator={loadingIndicator} | ||
/> | ||
<InfinityTable.PlaceHolder | ||
height={underHeight} | ||
domNode={this.refUnderPlaceholder} | ||
loading={loading && !fullLoading} | ||
loadingIndicator={loadingIndicator} | ||
/> | ||
{total && pagination && ['top', 'both'].includes(pagination.position) && ( | ||
<Pagination | ||
showQuickJumper | ||
{...pagination} | ||
onChange={this.scrollToPage} | ||
current={currentPage} | ||
pageSize={pageSize} | ||
showSizeChanger={false} | ||
total={total} | ||
className={`infinity-page-table-pagination ${ | ||
pagination && pagination.className ? pagination.className : '' | ||
}`} | ||
/> | ||
)} | ||
<Table | ||
rowKey={record => record.key} | ||
{...rest} | ||
ref={forwardedRef} | ||
columns={columns} | ||
dataSource={dataSource} | ||
pagination={false} | ||
className={`infinity-page-table ${rest.className ? rest.className : ''}`} | ||
/> | ||
{total && pagination && [undefined, 'bottom', 'both'].includes(pagination.position) && ( | ||
<Pagination | ||
showQuickJumper | ||
{...pagination} | ||
onChange={this.scrollToPage} | ||
current={currentPage} | ||
pageSize={pageSize} | ||
showSizeChanger={false} | ||
total={total} | ||
className={`infinity-page-table-pagination ${ | ||
pagination && pagination.className ? pagination.className : '' | ||
}`} | ||
/> | ||
)} | ||
</Fragment> | ||
) | ||
} | ||
} | ||
InfinityTable.defaultProps = { | ||
// loading 效果, A visual react component for Loading status | ||
loadingIndicator: ( | ||
<div | ||
style={{ | ||
textAlign: 'center', | ||
paddingTop: 20, | ||
paddingBottom: 20, | ||
border: '1px solid #e8e8e8', | ||
}} | ||
> | ||
<Spin tip="Loading..." /> | ||
</div> | ||
), | ||
pagination: { defaultCurrent: 1 }, // 分页器 | ||
onScroll: null, // 滚动事件 | ||
onFetch: noop, // 滚动到低部触发Fetch方法 | ||
bidirectionalCachePages: Infinity, // 当前页前后的缓存页码数量,默认为无限 | ||
total: 0, // 总共数据条数 | ||
debug: false, // display console log for debug | ||
loading: false, // 是否loading状态 | ||
pageSize: 30, // 每次Loading数据量,pageSize * 3 = 最大真实DOM大小,Reality DOM row count | ||
// loading 效果, A visual react component for Loading status | ||
loadingIndicator: ( | ||
<div | ||
style={{ | ||
textAlign: 'center', | ||
paddingTop: 20, | ||
paddingBottom: 20, | ||
border: '1px solid #e8e8e8', | ||
}} | ||
> | ||
<Spin tip="Loading..." /> | ||
</div> | ||
), | ||
pagination: { defaultCurrent: 1 }, // 分页器 | ||
onScroll: null, // 滚动事件 | ||
onFetch: noop, // 滚动到低部触发Fetch方法 | ||
bidirectionalCachePages: Infinity, // 当前页前后的缓存页码数量,默认为无限 | ||
total: 0, // 总共数据条数 | ||
debug: false, // display console log for debug | ||
loading: false, // 是否loading状态 | ||
pageSize: 30, // 每次Loading数据量,pageSize * 3 = 最大真实DOM大小,Reality DOM row count | ||
} | ||
InfinityTable.propTypes = { | ||
// loading 效果 | ||
className: string, | ||
loadingIndicator: element, | ||
onScroll: func, // 滚动事件 | ||
pagination: oneOfType([ | ||
bool, | ||
shape({ | ||
className: string, | ||
position: oneOf(['both', 'top', 'bottom']), | ||
className: string, | ||
defaultCurrent: number, | ||
hideOnSinglePage: bool, | ||
itemRender: func, | ||
showQuickJumper: bool, | ||
showTotal: func, | ||
simple: bool, | ||
size: string, | ||
onChange: func, | ||
}), | ||
]), | ||
onFetch: func.isRequired, // 滚动触发Fetch方法 | ||
pageSize: number.isRequired, | ||
bidirectionalCachePages: number.isRequired, | ||
total: number.isRequired, | ||
dataSource: array.isRequired, | ||
columns: array.isRequired, | ||
forwardedRef: object, | ||
debug: bool, | ||
loading: bool.isRequired, | ||
// loading 效果 | ||
className: string, | ||
loadingIndicator: element, | ||
onScroll: func, // 滚动事件 | ||
pagination: oneOfType([ | ||
bool, | ||
shape({ | ||
className: string, | ||
position: oneOf(['both', 'top', 'bottom']), | ||
className: string, | ||
defaultCurrent: number, | ||
hideOnSinglePage: bool, | ||
itemRender: func, | ||
showQuickJumper: bool, | ||
showTotal: func, | ||
simple: bool, | ||
size: string, | ||
onChange: func, | ||
}), | ||
]), | ||
onFetch: func.isRequired, // 滚动触发Fetch方法 | ||
pageSize: number.isRequired, | ||
bidirectionalCachePages: number.isRequired, | ||
total: number.isRequired, | ||
dataSource: array.isRequired, | ||
columns: array.isRequired, | ||
forwardedRef: object, | ||
debug: bool, | ||
loading: bool.isRequired, | ||
} | ||
@@ -593,0 +577,0 @@ |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1602226
4
4035
- Removedantd-table-infinity@^1.1.2