vue-tree-polyfill
Version:
A vue tree component using virtual list.
288 lines (281 loc) • 8.9 kB
text/typescript
import { ref } from "vue"
import { AnyPropsArrayType, INonReactiveData, IgnoreType, TreeNodeKeyType } from "../types"
import { TreeNode } from ".."
import { ITreeNodeOptions } from "../store/tree-node"
import { FilterFunctionType } from "../store/tree-store"
import { TreeProps } from "../components/Tree.vue"
type IUsePublicTreeAPIProps = Required<Pick<TreeProps,
'selectable' |
'checkable' |
'separator' |
'ignoreMode' |
'titleField' |
'keyField' |
'showUnloadCheckedNodes' |
'unloadDataList'
>> & Pick<TreeProps, 'modelValue' | 'filterMethod' | 'load'>
export const usePublicTreeAPI = (
nonReactive: INonReactiveData,
props: IUsePublicTreeAPIProps,
options: {
resetSpaceHeights: () => void,
updateExpandedKeys: () => void,
updateBlockData: () => void,
updateRender: () => void,
},
) => {
const {
resetSpaceHeights,
updateExpandedKeys,
updateBlockData,
updateRender,
} = options
const unloadCheckedNodes = ref<TreeNode[]>([])
const isRootLoading = ref(false)
/** 使用此方法重置树数据,可避免大量数据被 vue 监听 */
function setData(data: AnyPropsArrayType): void {
resetSpaceHeights()
let checkableUnloadKeys: TreeNodeKeyType | TreeNodeKeyType[] | null = null
let selectableUnloadKey: TreeNodeKeyType | null = null
if (props.checkable) {
if (Array.isArray(props.modelValue)) {
checkableUnloadKeys = props.modelValue.concat()
} else if (typeof props.modelValue === 'string') {
checkableUnloadKeys =
props.modelValue === ''
? []
: props.modelValue.split(props.separator)
}
} else if (props.selectable && !Array.isArray(props.modelValue)) {
selectableUnloadKey = props.modelValue as TreeNodeKeyType | null
}
nonReactive.store.setData(
data,
selectableUnloadKey,
checkableUnloadKeys as TreeNodeKeyType[]
)
updateExpandedKeys()
}
function setChecked(key: TreeNodeKeyType, value: boolean): void {
nonReactive.store.setChecked(key, value)
}
function setCheckedKeys(keys: TreeNodeKeyType[], value: boolean): void {
nonReactive.store.setCheckedKeys(keys, value)
}
function checkAll(): void {
nonReactive.store.checkAll()
}
function clearChecked(): void {
nonReactive.store.clearChecked()
}
function setSelected(key: TreeNodeKeyType, value: boolean): void {
nonReactive.store.setSelected(key, value)
}
function clearSelected(): void {
nonReactive.store.clearSelected()
}
function setExpand(
key: TreeNodeKeyType,
value: boolean,
expandParent: boolean = true
): void {
nonReactive.store.setExpand(key, value, expandParent)
}
function setExpandKeys(keys: TreeNodeKeyType[], value: boolean): void {
nonReactive.store.setExpandKeys(keys, value)
}
function setExpandAll(value: boolean): void {
nonReactive.store.setExpandAll(value)
}
function getCheckedNodes(ignoreMode?: IgnoreType): TreeNode[] {
ignoreMode = ignoreMode || props.ignoreMode
return nonReactive.store.getCheckedNodes(ignoreMode)
}
function getCheckedKeys(ignoreMode?: IgnoreType): TreeNodeKeyType[] {
ignoreMode = ignoreMode || props.ignoreMode
return nonReactive.store.getCheckedKeys(ignoreMode)
}
function getIndeterminateNodes(): TreeNode[] {
return nonReactive.store.getIndeterminateNodes()
}
function getSelectedNode(): TreeNode | null {
return nonReactive.store.getSelectedNode()
}
function getSelectedKey(): TreeNodeKeyType | null {
return nonReactive.store.getSelectedKey()
}
function getExpandNodes(): TreeNode[] {
return nonReactive.store.getExpandNodes()
}
function getExpandKeys(): TreeNodeKeyType[] {
return nonReactive.store.getExpandKeys()
}
function getCurrentVisibleNodes(): TreeNode[] {
return nonReactive.store.flatData.filter(node => node._filterVisible)
}
function getNode(key: TreeNodeKeyType): TreeNode | null {
return nonReactive.store.getNode(key)
}
/** 返回树形结构的节点数据 */
function getTreeData(): TreeNode[] {
return nonReactive.store.data
}
/** 返回扁平化后的节点数据 */
function getFlatData(): TreeNode[] {
return nonReactive.store.flatData
}
function getNodesCount(): number {
return nonReactive.store.flatData.length
}
function insertBefore(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
referenceKey: TreeNodeKeyType
): TreeNode | null {
return nonReactive.store.insertBefore(insertedNode, referenceKey)
}
function insertAfter(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
referenceKey: TreeNodeKeyType
): TreeNode | null {
return nonReactive.store.insertAfter(insertedNode, referenceKey)
}
function append(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
parentKey: TreeNodeKeyType
): TreeNode | null {
return nonReactive.store.append(insertedNode, parentKey)
}
function prepend(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
parentKey: TreeNodeKeyType
): TreeNode | null {
return nonReactive.store.prepend(insertedNode, parentKey)
}
function remove(removedKey: TreeNodeKeyType): TreeNode | null {
return nonReactive.store.remove(removedKey)
}
function filter(keyword: string, filterMethod?: FilterFunctionType): void {
const defaultFilterMethod = (keyword: string, node: TreeNode) => {
const title = node[props.titleField]
if (title == null || !title.toString) return false
return (
(title.toString() as string)
.toLowerCase()
.indexOf(keyword.toLowerCase()) > -1
)
}
const finalFilterMethod = filterMethod || props.filterMethod || defaultFilterMethod
nonReactive.store.filter(keyword, finalFilterMethod)
}
/**
* 展示已选节点
*/
function showCheckedNodes(showUnloadCheckedNodes?: boolean): void {
if (!props.checkable) return
showUnloadCheckedNodes =
showUnloadCheckedNodes == null
? props.showUnloadCheckedNodes
: showUnloadCheckedNodes
const checkedNodesCache = nonReactive.store.getCheckedNodes()
nonReactive.store.filter('', (keyword, node) => node.checked)
if (!showUnloadCheckedNodes) return
const unloadKeys = nonReactive.store.getUnloadCheckedKeys()
if (unloadKeys.length) {
const unloadNodes: TreeNode[] = unloadKeys.map(key => {
const queryList = props.unloadDataList.concat(checkedNodesCache)
let title = key
queryList.some((query: any) => {
if (
query[props.keyField] === key &&
query[props.titleField] != null
) {
title = query[props.titleField]
return true
}
return false
})
return new TreeNode(
{
[props.keyField]: key,
[props.titleField]: title,
checked: true,
isLeaf: true
},
null,
props.keyField,
!!props.load
)
})
unloadCheckedNodes.value = unloadNodes as TreeNode[]
nonReactive.blockNodes = nonReactive.blockNodes.concat(unloadNodes)
updateBlockData()
updateRender()
}
}
/**
* 从远程加载根节点
*/
function loadRootNodes(): Promise<void> {
isRootLoading.value = true
return new Promise((resolve, reject) => {
if (props.load) props.load(null, resolve, reject)
})
.then(root => {
if (Array.isArray(root)) {
setData(root as AnyPropsArrayType)
}
})
.catch(() => {})
.then(() => {
isRootLoading.value = false
})
}
/**
* 更新单个节点
*/
function updateNode(key: TreeNodeKeyType, newNode: ITreeNodeOptions) {
return nonReactive.store.updateNode(key, newNode)
}
/**
* 更新多个节点
*/
function updateNodes(newNodes: ITreeNodeOptions[]) {
return nonReactive.store.updateNodes(newNodes)
}
return {
unloadCheckedNodes,
isRootLoading,
setData,
setChecked,
setCheckedKeys,
checkAll,
clearChecked,
setSelected,
clearSelected,
setExpand,
setExpandKeys,
setExpandAll,
getCheckedNodes,
getCheckedKeys,
getIndeterminateNodes,
getSelectedNode,
getSelectedKey,
getExpandNodes,
getExpandKeys,
getCurrentVisibleNodes,
getNode,
getTreeData,
getFlatData,
getNodesCount,
insertBefore,
insertAfter,
append,
prepend,
remove,
filter,
showCheckedNodes,
loadRootNodes,
updateNode,
updateNodes,
}
}