vue-virtualized-table-booway
Version:
The second version of implementation of `vue-virtual-table` component, it was inspired from [rc-table](https://github.com/react-component/table) and [ant-table](https://ant.design/components/table), API design is 60%+ consistent. Or you could think I tran
189 lines (159 loc) • 4.28 kB
JavaScript
import { isObject, isValidArray } from '../utils/type'
import { forceScrollTop } from '../utils/dom/scroll'
export function data() {
return {
scrollToRowIndex: this.scrollToRow,
virtualizedData: []
}
}
export function created() {
const { useVirtual, rowHeight, scroll = {} } = this
if (useVirtual && !scroll.y) {
throw new ReferenceError(
`When open 'useVirtual' mode the property 'scroll.y' must be set as number to fixed header and calculates how many items should be render in table!`
)
}
if (useVirtual && !rowHeight) {
throw new ReferenceError(
`When open 'useVirtual' mode the property 'rowHeight' must be set as number to fix the height of per table row!`
)
}
this.__prevStartIndex = 0
}
export const computed = {
virtualized() {
return !!(
this.useVirtual &&
this.rowHeight &&
this.scroll &&
this.scroll.y &&
typeof this.scroll.y === 'number'
)
},
currentDataSource() {
if (isObject(this.expandable)) {
return this.entireDataSource || []
}
return this.dataSource
},
currentDataLength() {
return isValidArray(this.currentDataSource)
? this.currentDataSource.length
: 0
},
wrapperSize() {
return this.virtualized ? this.currentDataLength * this.rowHeight : 0
},
virtualVisibleItemsSize() {
return this.virtualized ? Math.ceil(this.scroll.y / this.rowHeight) : 0
},
wrapperScrollTop() {
return this.virtualized ? this.scrollToRowIndex * this.rowHeight : 0
},
maxSliceBlockStep() {
return this.virtualized
? Math.ceil(
this.wrapperSize / (this.rowHeight * this.virtualVisibleItemsSize)
)
: 0
}
}
export const watch = {
dataSource: {
immediate: true,
handler(data) {
if (data && data.length) {
if (~this.scrollToRow) {
this.updateScrollToRowIndex()
}
this.updateVirtualizedData(true)
} else {
this.virtualizedData = []
this.entireDataSource = []
}
}
},
entireDataSource(data) {
if (data && data.length) {
this.updateVirtualizedData(true)
} else {
this.virtualizedData = []
}
},
scrollToRow() {
this.updateScrollToRowIndex()
}
}
export const methods = {
getVisibleRange(offset) {
let start = Math.floor(offset / this.rowHeight) || 0
const N = this.virtualVisibleItemsSize || 0
const MIN_INDEX = 0
let end = start + N || 0
this.__CURRENT_N_STEP = Math.ceil(
offset / (this.rowHeight * this.virtualVisibleItemsSize)
)
if (start < N) {
start = MIN_INDEX
end += N
} else {
start = start - N
end += N
}
return {
start,
end
}
},
updateVirtualizedData(scrollTop = 0, forceUpdate) {
if (scrollTop === true) {
scrollTop = this.__PREV_SCROLL_TOP
forceUpdate = true
}
const { start, end } = this.getVisibleRange(scrollTop)
const shouldUpdate =
// this.__PREV_START_INDEX !== start ||
this.__CURRENT_N_STEP !== this.__PREV__CURRENT_N_STEP
if (!shouldUpdate && !forceUpdate) {
return
}
this.__PREV__CURRENT_N_STEP = this.__CURRENT_N_STEP
this.__PREV_START_INDEX = start
this.__PREV_SCROLL_TOP = scrollTop
this.scrollToRowIndex = start
this.virtualizedData = this.currentDataSource.slice(start, end)
},
updateScrollToRowIndex() {
this.$nextTick(() => {
if (this.$ready && ~this.scrollToRow && this.rowHeight) {
const { scrollBodyRef } = this.$refs
if (scrollBodyRef) {
forceScrollTop(this.scrollToRow * this.rowHeight, scrollBodyRef)
}
}
})
},
renderVirtualizedWrapper(baseTable) {
return (
<div
class={`${this.prefixCls}-virtual-wrapper`}
style={{
position: 'relative',
height: this.wrapperSize + 'px'
}}
>
<div
class={`${this.prefixCls}-virtual-inner`}
style={{
// position: "relative",
willChange: 'transform',
WebkitOverflowScrolling: 'touch',
transform: `translateY(${this.wrapperScrollTop}px)`
}}
>
{baseTable}
</div>
</div>
)
}
}