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
424 lines (358 loc) • 10.5 kB
JavaScript
import {
flattenMap,
flattenData,
insertDataFromStart,
genExpandedKeyPaths,
findValidChildrenKeys
} from '../utils/expand'
import { isString, isNumber, isObject, isValidArray } from '../utils/type'
export function data() {
const childrenColumnName =
(isObject(this.expandable) && this.expandable.childrenColumnName) ||
'children'
this.childrenColumnName = childrenColumnName
if (isObject(this.expandable)) {
return {
entireDataSource: [],
expandedRowKeys: []
}
}
return {}
}
// only watch those properties form `expandable` prop
export const watch = {
dataSource: {
immediate: true,
handler(data) {
if (!isValidArray(data)) {
return this._clearExpansionSideEffects()
}
this._initDataSourceState(data)
if (isObject(this.expandable)) {
this.entireDataSource = this._updateDataByExpandable()
this.expandedRowKeys = this._updateExpandedKeys()
if (!this.__initializedEntireData) {
this.__initializedEntireData = true
}
}
}
},
'expandable.defaultExpandAllRows'(value) {
this.toggleExpandAll(value)
},
'expandable.expandDepth'(value) {
this.toggleExpandDepth(value)
},
'expandable.expandedRowKeys'(value) {
this.toggleExpandKeys(value)
}
}
export function created() {}
export const methods = {
/**
* rowKey 是否已展开,判断当前行是否是展开状态
* @param {RowKey} rowKey
*/
isRowExpanded(rowKey) {
return this.expandedRowKeys.includes(this.adaptRowKey(rowKey))
},
/**
* 根据传入行获取当前行的展开路径
* @param {RowModel} record
*/
getExpandedKeyPaths(record) {
const rowKey = this.getRowKey(record)
const paths = (this.flattenedPathMap || {})[rowKey]
return isValidArray(paths)
? paths
: genExpandedKeyPaths(record, this.getRowKey, this.childrenColumnName)
},
/**
* 根据 key 找到当前 row 在 data 中的索引
* @param {RowModel[]} data
* @param {string} key
*/
findIndexByKey(data, key) {
return data.findIndex((item) => this.getRowKey(item) === key)
},
/**
* 根据 key 找到当前 row 数据
* @param {RowModel|string} rowOrKey
*/
findItemByKey(rowOrKey) {
if (isObject(rowOrKey)) return rowOrKey
return this.flattenedData.find((item, index) => {
if (this.getRowKey(item, index) === rowOrKey) {
return item
}
})
},
/**
* 适配 row key
* @param {RowModel|string} rowOrRowKey
* @returns {string}
*/
adaptRowKey(rowOrRowKey) {
if (isObject(rowOrRowKey)) {
rowOrRowKey = this.getRowKey(rowOrRowKey)
}
return rowOrRowKey
},
/**
* 适配 key 到 row
* @param {RowModel|string} rowOrRowKey
* @returns {RowModel}
*/
adaptKeyToRow(rowOrRowKey) {
if (rowOrRowKey && isString(rowOrRowKey)) {
rowOrRowKey = this.findItemByKey(rowOrRowKey)
}
return rowOrRowKey
},
/**
* 通过传入 key 获取下一个 row
* @param {string} rowKey
* @param {boolean} returnPrevIfNoNext
*/
getNextItemByKey(rowKey, returnPrevIfNoNext) {
const currIndex = this.findIndexByKey(
this.flattenedData,
this.adaptRowKey(rowKey)
)
const nextItem = this.flattenedData[currIndex + 1]
if (nextItem) {
return nextItem
}
if (returnPrevIfNoNext) {
return this.getPrevItemByKey(rowKey, currIndex)
}
},
/**
* 通过传入 key 获取上一个 row
* @param {string} rowKey
* @param {number} index
*/
getPrevItemByKey(rowKey, index) {
const currIndex = isNumber(index)
? index
: this.findIndexByKey(this.flattenedData, this.adaptRowKey(rowKey))
const prevItem = this.flattenedData[currIndex - 1]
if (prevItem) {
return prevItem
}
},
/**
* 展开/收起指定 rowKey,切换当前行展开收起状态
* @param {RowKey} rowKey
* @param {boolean} state
* @param {Event} event
*/
toggleRowExpansion(rowKey, state, event) {
if (!rowKey) return
let record = this.adaptKeyToRow(rowKey)
rowKey = this.adaptRowKey(record)
const expandedRowKeys = this.expandedRowKeys
const isExpanded = this.isRowExpanded(rowKey)
const toggle = () => {
if (isExpanded) {
if (isObject(record) && isValidArray(record.children)) {
const childRowKeys = findValidChildrenKeys(
record.children,
this.getRowKey,
this.childrenColumnName
)
if (childRowKeys && childRowKeys.length) {
for (let i = 0; i < childRowKeys.length; i++) {
const childIndex = expandedRowKeys.indexOf(childRowKeys[i])
if (~childIndex) {
expandedRowKeys.splice(childIndex, 1)
}
}
}
}
expandedRowKeys.splice(expandedRowKeys.indexOf(rowKey), 1)
} else {
expandedRowKeys.push(rowKey)
}
}
if (state !== undefined) {
if (state) {
if (!isExpanded) {
expandedRowKeys.push(rowKey)
}
} else {
if (isExpanded) {
expandedRowKeys.splice(expandedRowKeys.indexOf(rowKey), 1)
}
}
} else {
toggle()
}
this.entireDataSource = this._genDataByKeys()
this.$emit('expand-change', record, state, expandedRowKeys, event)
},
/**
* 展开/收起 全部
* @param {boolean} expand
*/
toggleExpandAll(expand) {
if (expand) {
// 展开全部 dataSource 既是展平的 flattenedData
this.expandedRowKeys = this._genExpandedKeys(
(this.entireDataSource = this.flattenedData)
)
} else {
// 默认 dataSource 是未展开的数据所以直接赋值
this.entireDataSource = this.dataSource
this.expandedRowKeys = []
}
},
/**
* 展开指定深度层级的数据并返回
* @param {number} depth
*/
toggleExpandDepth(depth) {
this.entireDataSource = this._genDataByDepth(depth)
this.expandedRowKeys = this._genExpandedKeysWithDepth(
this.entireDataSource,
depth
)
},
/**
* 根据 nextExpandedKeys 重新生成 entireDataSource
* @param {string[]} nextExpandedKeys
*/
toggleExpandKeys(nextExpandedKeys) {
this.entireDataSource = this._genDataByKeys(nextExpandedKeys)
this.expandedRowKeys = nextExpandedKeys
},
_initDataSourceState(data = []) {
if (!isValidArray(data)) {
this.flattenedData = null
this.flattenedPathMap = null
} else {
this.flattenedData = flattenData(data, this.childrenColumnName)
this.flattenedData.forEach((item, index) => {
item.__flatIndex = index
})
this.flattenedPathMap = flattenMap(
data,
this.getRowKey,
this.childrenColumnName
)
if (process.env.NODE_ENV === 'development') {
console.log('flattened: ', this.flattenedData, this.flattenedPathMap)
}
}
},
_updateDataByExpandable() {
if (isValidArray(this.expandedRowKeys) || this.__initializedEntireData) {
return this._genDataByKeys(this.expandedRowKeys)
}
const {
expandDepth = null,
expandedRowKeys = [],
defaultExpandAllRows = null
} = this.expandable || {}
let entireDataSource = this.dataSource
if (isValidArray(expandedRowKeys)) {
entireDataSource = this._genDataByKeys(expandedRowKeys)
} else if (defaultExpandAllRows) {
entireDataSource = this.flattenedData || []
} else if (expandDepth && isNumber(expandDepth)) {
entireDataSource = this._genDataByDepth(expandDepth)
}
return entireDataSource
},
_updateExpandedKeys() {
if (isValidArray(this.expandedRowKeys) || this.__initializedEntireData) {
return this.expandedRowKeys
}
let nextExpandedRowKeys = this.expandedRowKeys
const {
expandDepth = null,
expandedRowKeys = [],
defaultExpandAllRows = null
} = this.expandable || {}
if (isValidArray(expandedRowKeys)) {
nextExpandedRowKeys = expandedRowKeys
} else if (typeof defaultExpandAllRows === 'boolean') {
nextExpandedRowKeys = this._genExpandedKeys(this.entireDataSource)
} else if (expandDepth && isNumber(expandDepth)) {
nextExpandedRowKeys = this._genExpandedKeysWithDepth(
this.entireDataSource,
expandDepth
)
}
return nextExpandedRowKeys
},
/**
* _genExpandedKeys
* @param {{}[]} data
*/
_genExpandedKeys(data) {
return (data || this.entireDataSource || []).reduce((keys, item, index) => {
const children = item[this.childrenColumnName]
if (isValidArray(children)) {
keys = [...keys, this.getRowKey(item, index)]
}
return keys
}, [])
},
/**
* _genExpandedKeys
* @param {{}[]} data
* @param {number} depth
*/
_genExpandedKeysWithDepth(data, depth) {
return (data || this.entireDataSource || [])
.filter(
(item) =>
item.__depth < depth && isValidArray(item[this.childrenColumnName])
)
.map((item, index) => this.getRowKey(item, index))
},
/**
* _genDataByDepth
* @param {number} depth
*/
_genDataByDepth(depth) {
return (this.flattenedData || []).filter((item) => item.__depth <= depth)
},
/**
* _genDataByKeys
* @param {(string|number)[]} keys
*/
_genDataByKeys(keys) {
const expandedRowKeys = keys || this.expandedRowKeys
if (!expandedRowKeys.length) {
return this.dataSource
} else {
let paths = []
// 数据的初始形态
let result = this.dataSource.slice()
// 获取完整的树形路径集合
expandedRowKeys.forEach((key) => {
paths.push(...((this.flattenedPathMap || {})[key] || []))
})
// 对路径集合去重复
paths = [...new Set(paths)]
while (paths.length) {
const dataIndex = this.findIndexByKey(result, paths.shift())
if (~dataIndex) {
const item = result[dataIndex] || {}
const children = item[this.childrenColumnName]
result = insertDataFromStart(result, dataIndex, children)
}
}
return result
}
},
_clearExpansionSideEffects() {
this.entireDataSource = this.flattenedData = this.expandedRowKeys = []
this.flattenedPathMap = this.__initializedEntireData = null
}
}
export function beforeDestroy() {
this._clearExpansionSideEffects()
}