ant-design-vue/components/vc-tree-select/src/Base/BasePopup.jsx

285 lines
7.5 KiB
Vue

import warning from 'warning'
import PropTypes from '../../../_util/vue-types'
import { Tree } from '../../../vc-tree'
import BaseMixin from '../../../_util/BaseMixin'
// export const popupContextTypes = {
// onPopupKeyDown: PropTypes.func.isRequired,
// onTreeNodeSelect: PropTypes.func.isRequired,
// onTreeNodeCheck: PropTypes.func.isRequired,
// }
function getDerivedStateFromProps (nextProps, prevState) {
const { _prevProps: prevProps = {},
_loadedKeys: loadedKeys,
_expandedKeyList: expandedKeyList,
_cachedExpandedKeyList: cachedExpandedKeyList,
} = prevState || {}
const {
valueList, valueEntities, keyEntities,
treeExpandedKeys, filteredTreeNodes, searchValue,
} = nextProps
const newState = {
_prevProps: { ...nextProps },
}
// Check value update
if (valueList !== prevProps.valueList) {
newState._keyList = valueList
.map(({ value }) => valueEntities[value])
.filter(entity => entity)
.map(({ key }) => key)
}
// Show all when tree is in filter mode
if (
!treeExpandedKeys &&
filteredTreeNodes &&
filteredTreeNodes.length &&
filteredTreeNodes !== prevProps.filteredTreeNodes
) {
newState._expandedKeyList = Object.keys(keyEntities)
}
// Cache `expandedKeyList` when filter set
if (searchValue && !prevProps.searchValue) {
newState._cachedExpandedKeyList = expandedKeyList
} else if (!searchValue && prevProps.searchValue && !treeExpandedKeys) {
newState._expandedKeyList = cachedExpandedKeyList || []
newState._cachedExpandedKeyList = []
}
// Use expandedKeys if provided
if (prevProps.treeExpandedKeys !== treeExpandedKeys) {
newState._expandedKeyList = treeExpandedKeys
}
// Clean loadedKeys if key not exist in keyEntities anymore
if (nextProps.loadData) {
newState._loadedKeys = loadedKeys.filter(key => key in keyEntities)
}
return newState
}
const BasePopup = {
mixins: [BaseMixin],
name: 'BasePopup',
props: {
prefixCls: PropTypes.string,
upperSearchValue: PropTypes.string,
valueList: PropTypes.array,
searchHalfCheckedKeys: PropTypes.array,
valueEntities: PropTypes.object,
keyEntities: PropTypes.object,
treeIcon: PropTypes.bool,
treeLine: PropTypes.bool,
treeNodeFilterProp: PropTypes.string,
treeCheckable: PropTypes.any,
treeCheckStrictly: PropTypes.bool,
treeDefaultExpandAll: PropTypes.bool,
treeDefaultExpandedKeys: PropTypes.array,
treeExpandedKeys: PropTypes.array,
loadData: PropTypes.func,
multiple: PropTypes.bool,
// onTreeExpand: PropTypes.func,
searchValue: PropTypes.string,
treeNodes: PropTypes.any,
filteredTreeNodes: PropTypes.any,
notFoundContent: PropTypes.string,
ariaId: PropTypes.string,
switcherIcon: PropTypes.any,
// HOC
renderSearch: PropTypes.func,
// onTreeExpanded: PropTypes.func,
__propsSymbol__: PropTypes.any,
},
inject: {
vcTreeSelect: { default: {}},
},
watch: {
__propsSymbol__ () {
const state = getDerivedStateFromProps(this.$props, this.$data)
this.setState(state)
},
},
data () {
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__')
const {
treeDefaultExpandAll, treeDefaultExpandedKeys,
keyEntities,
} = this.$props
// TODO: make `expandedKeyList` control
let expandedKeyList = treeDefaultExpandedKeys
if (treeDefaultExpandAll) {
expandedKeyList = Object.keys(keyEntities)
}
const state = {
_keyList: [],
_expandedKeyList: expandedKeyList,
// Cache `expandedKeyList` when tree is in filter. This is used in `getDerivedStateFromProps`
_cachedExpandedKeyList: [], // eslint-disable-line react/no-unused-state
_loadedKeys: [],
_prevProps: {},
}
return {
...state,
...getDerivedStateFromProps(this.$props, state),
}
},
methods: {
onTreeExpand (expandedKeyList) {
const { treeExpandedKeys } = this.$props
// Set uncontrolled state
if (!treeExpandedKeys) {
this.setState({ _expandedKeyList: expandedKeyList }, () => {
this.__emit('treeExpanded')
})
}
this.__emit('treeExpand', expandedKeyList)
},
onLoad (loadedKeys) {
this.setState({ _loadedKeys: loadedKeys })
},
/**
* Not pass `loadData` when searching. To avoid loop ajax call makes browser crash.
*/
getLoadData () {
const { loadData, searchValue } = this.$props
if (searchValue) return null
return loadData
},
/**
* This method pass to Tree component which is used for add filtered class
* in TreeNode > li
*/
filterTreeNode (treeNode) {
const { upperSearchValue, treeNodeFilterProp } = this.$props
const filterVal = treeNode[treeNodeFilterProp]
if (typeof filterVal === 'string') {
return upperSearchValue && (filterVal).toUpperCase().indexOf(upperSearchValue) !== -1
}
return false
},
renderNotFound () {
const { prefixCls, notFoundContent } = this.$props
return (
<span class={`${prefixCls}-not-found`}>
{notFoundContent}
</span>
)
},
},
render () {
const { _keyList: keyList, _expandedKeyList: expandedKeyList, _loadedKeys: loadedKeys } = this.$data
const {
prefixCls,
treeNodes, filteredTreeNodes,
treeIcon, treeLine, treeCheckable, treeCheckStrictly, multiple,
ariaId,
renderSearch,
switcherIcon,
searchHalfCheckedKeys,
} = this.$props
const { vcTreeSelect: {
onPopupKeyDown,
onTreeNodeSelect,
onTreeNodeCheck,
}} = this
const loadData = this.getLoadData()
const treeProps = {}
if (treeCheckable) {
treeProps.checkedKeys = keyList
} else {
treeProps.selectedKeys = keyList
}
let $notFound
let $treeNodes
if (filteredTreeNodes) {
if (filteredTreeNodes.length) {
treeProps.checkStrictly = true
$treeNodes = filteredTreeNodes
// Fill halfCheckedKeys
if (treeCheckable && !treeCheckStrictly) {
treeProps.checkedKeys = {
checked: keyList,
halfChecked: searchHalfCheckedKeys,
}
}
} else {
$notFound = this.renderNotFound()
}
} else if (!treeNodes.length) {
$notFound = this.renderNotFound()
} else {
$treeNodes = treeNodes
}
let $tree
if ($notFound) {
$tree = $notFound
} else {
const treeAllProps = {
props: {
prefixCls: `${prefixCls}-tree`,
showIcon: treeIcon,
showLine: treeLine,
selectable: !treeCheckable,
checkable: treeCheckable,
checkStrictly: treeCheckStrictly,
multiple: multiple,
loadData: loadData,
loadedKeys: loadedKeys,
expandedKeys: expandedKeyList,
filterTreeNode: this.filterTreeNode,
switcherIcon: switcherIcon,
...treeProps,
__propsSymbol__: Symbol(),
children: $treeNodes,
},
on: {
select: onTreeNodeSelect,
check: onTreeNodeCheck,
expand: this.onTreeExpand,
load: this.onLoad,
},
}
$tree = (
<Tree
{...treeAllProps}
/>
)
}
return (
<div
role='listbox'
id={ariaId}
onKeydown={onPopupKeyDown}
tabIndex={-1}
>
{renderSearch ? renderSearch() : null}
{$tree}
</div>
)
},
}
export default BasePopup