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

286 lines
7.5 KiB
Vue

import { inject } from 'vue';
import warning from 'warning';
import PropTypes from '../../../_util/vue-types';
import Tree from '../../../vc-tree';
import BaseMixin from '../../../_util/BaseMixin';
import { createRef } from '../util';
// export const popupContextTypes = {
// onPopupKeyDown: PropTypes.func.isRequired,
// onTreeNodeSelect: PropTypes.func.isRequired,
// onTreeNodeCheck: PropTypes.func.isRequired,
// }
function getDerivedState(nextProps, prevState) {
const {
_prevProps: prevProps = {},
_loadedKeys: loadedKeys,
_expandedKeyList: expandedKeyList,
_cachedExpandedKeyList: cachedExpandedKeyList,
} = prevState || {};
const {
valueList,
valueEntities,
keyEntities,
treeExpandedKeys,
filteredTreeNodes,
upperSearchValue,
} = 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 = [...keyEntities.keys()];
}
// Cache `expandedKeyList` when filter set
if (upperSearchValue && !prevProps.upperSearchValue) {
newState._cachedExpandedKeyList = expandedKeyList;
} else if (!upperSearchValue && prevProps.upperSearchValue && !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 => keyEntities.has(key));
}
return newState;
}
const BasePopup = {
mixins: [BaseMixin],
inheritAttrs: false,
name: 'BasePopup',
props: {
prefixCls: PropTypes.string,
upperSearchValue: PropTypes.string,
valueList: PropTypes.array,
searchHalfCheckedKeys: PropTypes.array,
valueEntities: PropTypes.object,
keyEntities: Map,
treeIcon: PropTypes.looseBool,
treeLine: PropTypes.looseBool,
treeNodeFilterProp: PropTypes.string,
treeCheckable: PropTypes.any,
treeCheckStrictly: PropTypes.looseBool,
treeDefaultExpandAll: PropTypes.looseBool,
treeDefaultExpandedKeys: PropTypes.array,
treeExpandedKeys: PropTypes.array,
loadData: PropTypes.func,
multiple: PropTypes.looseBool,
// onTreeExpand: PropTypes.func,
searchValue: PropTypes.string,
treeNodes: PropTypes.any,
filteredTreeNodes: PropTypes.any,
notFoundContent: PropTypes.any,
ariaId: PropTypes.string,
switcherIcon: PropTypes.any,
// HOC
renderSearch: PropTypes.func,
// onTreeExpanded: PropTypes.func,
__propsSymbol__: PropTypes.any,
},
setup() {
return {
vcTreeSelect: inject('vcTreeSelect', {}),
};
},
watch: {
__propsSymbol__() {
const state = getDerivedState(this.$props, this.$data);
this.setState(state);
},
},
data() {
this.treeRef = createRef();
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__');
const { treeDefaultExpandAll, treeDefaultExpandedKeys, keyEntities } = this.$props;
// TODO: make `expandedKeyList` control
let expandedKeyList = treeDefaultExpandedKeys;
if (treeDefaultExpandAll) {
expandedKeyList = [...keyEntities.keys()];
}
const state = {
_keyList: [],
_expandedKeyList: expandedKeyList,
// Cache `expandedKeyList` when tree is in filter. This is used in `getDerivedState`
_cachedExpandedKeyList: [],
_loadedKeys: [],
_prevProps: {},
};
return {
...state,
...getDerivedState(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 });
},
getTree() {
return this.treeRef.current;
},
/**
* Not pass `loadData` when searching. To avoid loop ajax call makes browser crash.
*/
getLoadData() {
const { loadData, upperSearchValue } = this.$props;
if (upperSearchValue) 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 || !treeNodes.length) {
$notFound = this.renderNotFound();
} else {
$treeNodes = treeNodes;
}
let $tree;
if ($notFound) {
$tree = $notFound;
} else {
const treeAllProps = {
prefixCls: `${prefixCls}-tree`,
showIcon: treeIcon,
showLine: treeLine,
selectable: !treeCheckable,
checkable: treeCheckable,
checkStrictly: treeCheckStrictly,
multiple,
loadData,
loadedKeys,
expandedKeys: expandedKeyList,
filterTreeNode: this.filterTreeNode,
switcherIcon,
...treeProps,
children: $treeNodes,
onSelect: onTreeNodeSelect,
onCheck: onTreeNodeCheck,
onExpand: this.onTreeExpand,
onLoad: this.onLoad,
};
$tree = <Tree {...treeAllProps} ref={this.treeRef} __propsSymbol__={[]} />;
}
return (
<div role="listbox" id={ariaId} onKeydown={onPopupKeyDown} tabindex={-1}>
{renderSearch ? renderSearch() : null}
{$tree}
</div>
);
},
};
export default BasePopup;