feat: update tree and vc-tree
parent
2fb6667593
commit
3dc6b0745c
|
@ -0,0 +1,207 @@
|
|||
import omit from 'omit.js'
|
||||
import debounce from 'lodash/debounce'
|
||||
import PropTypes from '../_util/vue-types'
|
||||
import { conductExpandParent, convertTreeToEntities } from '../vc-tree/src/util'
|
||||
import Tree, { TreeProps } from './Tree'
|
||||
import { calcRangeKeys, getFullKeyList } from './util'
|
||||
import Icon from '../icon'
|
||||
import { initDefaultProps, getOptionProps } from '../_util/props-util'
|
||||
|
||||
// export type ExpandAction = false | 'click' | 'doubleClick';
|
||||
|
||||
// export interface DirectoryTreeProps extends TreeProps {
|
||||
// expandAction?: ExpandAction;
|
||||
// }
|
||||
|
||||
// export interface DirectoryTreeState {
|
||||
// expandedKeys?: string[];
|
||||
// selectedKeys?: string[];
|
||||
// }
|
||||
|
||||
function getIcon (h, props) {
|
||||
const { isLeaf, expanded } = props
|
||||
if (isLeaf) {
|
||||
return <Icon type='file' />
|
||||
}
|
||||
return <Icon type={expanded ? 'folder-open' : 'folder'} />
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ADirectoryTree',
|
||||
model: {
|
||||
prop: 'checkedKeys',
|
||||
event: 'check',
|
||||
},
|
||||
props: initDefaultProps({ ...TreeProps(), expandAction: PropTypes.oneOf([false, 'click', 'doubleClick']) }, {
|
||||
prefixCls: 'ant-tree',
|
||||
showIcon: true,
|
||||
expandAction: 'click',
|
||||
}),
|
||||
|
||||
// state: DirectoryTreeState;
|
||||
// onDebounceExpand: (event, node: AntTreeNode) => void;
|
||||
|
||||
// // Shift click usage
|
||||
// lastSelectedKey?: string;
|
||||
// cachedSelectedKeys?: string[];
|
||||
|
||||
data () {
|
||||
const props = getOptionProps(this)
|
||||
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props
|
||||
const { keyEntities } = convertTreeToEntities(this.$slots.default)
|
||||
const state = {}
|
||||
// Selected keys
|
||||
state._selectedKeys = props.selectedKeys || props.defaultSelectedKeys || []
|
||||
|
||||
// Expanded keys
|
||||
if (defaultExpandAll) {
|
||||
state._expandedKeys = getFullKeyList(props.children)
|
||||
} else if (defaultExpandParent) {
|
||||
state._expandedKeys = conductExpandParent(expandedKeys || defaultExpandedKeys, keyEntities)
|
||||
} else {
|
||||
state._expandedKeys = defaultExpandedKeys
|
||||
}
|
||||
|
||||
this.onDebounceExpand = debounce(this.expandFolderNode, 200, {
|
||||
leading: true,
|
||||
})
|
||||
return {
|
||||
_selectedKeys: [],
|
||||
_expandedKeys: [],
|
||||
...state,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
expandedKeys (val) {
|
||||
this.setState({ _expandedKeys: val })
|
||||
},
|
||||
selectedKeys (val) {
|
||||
this.setState({ _selectedKeys: val })
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onExpand (expandedKeys, info) {
|
||||
this.setUncontrolledState({ _expandedKeys: expandedKeys })
|
||||
|
||||
this.$emit('expand', expandedKeys, info)
|
||||
|
||||
return undefined
|
||||
},
|
||||
|
||||
onClick (event, node) {
|
||||
const { expandAction } = this.$props
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'click') {
|
||||
this.onDebounceExpand(event, node)
|
||||
}
|
||||
this.$emit('click', event, node)
|
||||
},
|
||||
|
||||
onDoubleClick (event, node) {
|
||||
const { expandAction } = this.$props
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'doubleClick') {
|
||||
this.onDebounceExpand(event, node)
|
||||
}
|
||||
|
||||
this.$emit('doubleclick', event, node)
|
||||
},
|
||||
|
||||
onSelect (keys, event) {
|
||||
const { multiple } = this.$props
|
||||
const children = this.$slots.default || []
|
||||
const { _expandedKeys: expandedKeys = [], _selectedKeys: selectedKeys = [] } = this.$data
|
||||
const { node, nativeEvent } = event
|
||||
const { eventKey = '' } = node
|
||||
|
||||
const newState = {}
|
||||
|
||||
// Windows / Mac single pick
|
||||
const ctrlPick = nativeEvent.ctrlKey || nativeEvent.metaKey
|
||||
const shiftPick = nativeEvent.shiftKey
|
||||
|
||||
// Generate new selected keys
|
||||
let newSelectedKeys = selectedKeys.slice()
|
||||
if (multiple && ctrlPick) {
|
||||
// Control click
|
||||
newSelectedKeys = keys
|
||||
this.lastSelectedKey = eventKey
|
||||
this.cachedSelectedKeys = newSelectedKeys
|
||||
} else if (multiple && shiftPick) {
|
||||
// Shift click
|
||||
newSelectedKeys = Array.from(new Set([
|
||||
...this.cachedSelectedKeys || [],
|
||||
...calcRangeKeys(children, expandedKeys, eventKey, this.lastSelectedKey),
|
||||
]))
|
||||
} else {
|
||||
// Single click
|
||||
newSelectedKeys = [eventKey]
|
||||
this.lastSelectedKey = eventKey
|
||||
this.cachedSelectedKeys = newSelectedKeys
|
||||
}
|
||||
newState._selectedKeys = newSelectedKeys
|
||||
|
||||
this.$emit('select', newSelectedKeys, event)
|
||||
|
||||
this.setUncontrolledState(newState)
|
||||
},
|
||||
|
||||
expandFolderNode (event, node) {
|
||||
const { _expandedKeys: expandedKeys = [] } = this.$data
|
||||
const { eventKey = '', expanded, isLeaf } = node
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return
|
||||
}
|
||||
|
||||
const newExpandedKeys = expandedKeys.slice()
|
||||
const index = newExpandedKeys.indexOf(eventKey)
|
||||
|
||||
if (expanded && index >= 0) {
|
||||
newExpandedKeys.splice(index, 1)
|
||||
} else if (!expanded && index === -1) {
|
||||
newExpandedKeys.push(eventKey)
|
||||
}
|
||||
|
||||
this.setUncontrolledState({
|
||||
_expandedKeys: newExpandedKeys,
|
||||
})
|
||||
this.$emit('expand', newExpandedKeys, {
|
||||
expanded: !expanded,
|
||||
node,
|
||||
nativeEvent: event.nativeEvent,
|
||||
})
|
||||
},
|
||||
|
||||
setUncontrolledState (state) {
|
||||
const newState = omit(state, Object.keys(getOptionProps(this)).map(p => `_${p}`))
|
||||
if (Object.keys(newState).length) {
|
||||
this.setState(newState)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { prefixCls, ...props } = getOptionProps(this)
|
||||
const { _expandedKeys: expandedKeys, _selectedKeys: selectedKeys } = this.$data
|
||||
const treeProps = {
|
||||
props: {
|
||||
icon: getIcon,
|
||||
...props,
|
||||
prefixCls,
|
||||
expandedKeys,
|
||||
selectedKeys,
|
||||
},
|
||||
class: `${prefixCls}-directory`,
|
||||
select: this.onSelect,
|
||||
click: this.onClick,
|
||||
doubleclick: this.onDoubleClick,
|
||||
expand: this.onExpand,
|
||||
}
|
||||
return (
|
||||
<Tree {...treeProps}>{this.$slots.default}</Tree>
|
||||
)
|
||||
},
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
import warning from 'warning'
|
||||
import { Tree as VcTree, TreeNode } from '../vc-tree'
|
||||
import animation from '../_util/openAnimation'
|
||||
import PropTypes from '../_util/vue-types'
|
||||
import { initDefaultProps, getOptionProps } from '../_util/props-util'
|
||||
|
||||
function TreeProps () {
|
||||
return {
|
||||
showLine: PropTypes.bool,
|
||||
/** 是否支持多选 */
|
||||
multiple: PropTypes.bool,
|
||||
/** 是否自动展开父节点 */
|
||||
autoExpandParent: PropTypes.bool,
|
||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||
checkStrictly: PropTypes.bool,
|
||||
/** 是否支持选中 */
|
||||
checkable: PropTypes.bool,
|
||||
/** 是否禁用树 */
|
||||
disabled: PropTypes.bool,
|
||||
/** 默认展开所有树节点 */
|
||||
defaultExpandAll: PropTypes.bool,
|
||||
/** 默认展开对应树节点 */
|
||||
defaultExpandParent: PropTypes.bool,
|
||||
/** 默认展开指定的树节点 */
|
||||
defaultExpandedKeys: PropTypes.arrayOf(String),
|
||||
/** (受控)展开指定的树节点 */
|
||||
expandedKeys: PropTypes.arrayOf(String),
|
||||
/** (受控)选中复选框的树节点 */
|
||||
checkedKeys: PropTypes.oneOfType(
|
||||
[
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
PropTypes.shape({
|
||||
checked: PropTypes.arrayOf(String),
|
||||
halfChecked: PropTypes.arrayOf(String),
|
||||
}).loose,
|
||||
]
|
||||
),
|
||||
/** 默认选中复选框的树节点 */
|
||||
defaultCheckedKeys: PropTypes.arrayOf(String),
|
||||
/** (受控)设置选中的树节点 */
|
||||
selectedKeys: PropTypes.arrayOf(String),
|
||||
/** 默认选中的树节点 */
|
||||
defaultSelectedKeys: PropTypes.arrayOf(String),
|
||||
selectable: PropTypes.bool,
|
||||
/** 展开/收起节点时触发 */
|
||||
// onExpand: (expandedKeys: string[], info: AntTreeNodeExpandedEvent) => void | PromiseLike<any>,
|
||||
/** 点击复选框触发 */
|
||||
// onCheck: (checkedKeys: string[] | { checked: string[]; halfChecked: string[] }, e: AntTreeNodeCheckedEvent) => void,
|
||||
/** 点击树节点触发 */
|
||||
// onSelect: (selectedKeys: string[], e: AntTreeNodeSelectedEvent) => void,
|
||||
/** 单击树节点触发 */
|
||||
// onClick: (e: React.MouseEvent<HTMLElement>, node: AntTreeNode) => void,
|
||||
/** 双击树节点触发 */
|
||||
// onDoubleClick: (e: React.MouseEvent<HTMLElement>, node: AntTreeNode) => void,
|
||||
/** filter some AntTreeNodes as you need. it should return true */
|
||||
filterAntTreeNode: PropTypes.func,
|
||||
/** 异步加载数据 */
|
||||
loadData: PropTypes.func,
|
||||
loadedKeys: PropTypes.arrayOf(String),
|
||||
// onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void,
|
||||
/** 响应右键点击 */
|
||||
// onRightClick: (options: AntTreeNodeMouseEvent) => void,
|
||||
/** 设置节点可拖拽(IE>8)*/
|
||||
draggable: PropTypes.bool,
|
||||
// /** 开始拖拽时调用 */
|
||||
// onDragStart: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** dragenter 触发时调用 */
|
||||
// onDragEnter: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** dragover 触发时调用 */
|
||||
// onDragOver: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** dragleave 触发时调用 */
|
||||
// onDragLeave: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** drop 触发时调用 */
|
||||
// onDrop: (options: AntTreeNodeMouseEvent) => void,
|
||||
showIcon: PropTypes.bool,
|
||||
icon: PropTypes.func,
|
||||
prefixCls: PropTypes.string,
|
||||
filterTreeNode: PropTypes.func,
|
||||
openAnimation: PropTypes.any,
|
||||
treeNodes: PropTypes.array,
|
||||
treeData: PropTypes.array,
|
||||
}
|
||||
}
|
||||
|
||||
export { TreeProps }
|
||||
|
||||
export default {
|
||||
name: 'ATree',
|
||||
model: {
|
||||
prop: 'checkedKeys',
|
||||
event: 'check',
|
||||
},
|
||||
props: initDefaultProps(TreeProps(), {
|
||||
prefixCls: 'ant-tree',
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
openAnimation: {
|
||||
on: animation,
|
||||
props: { appear: null },
|
||||
},
|
||||
}),
|
||||
created () {
|
||||
warning(
|
||||
!('treeNodes' in getOptionProps(this)),
|
||||
'`treeNodes` is deprecated. please use treeData instead.'
|
||||
)
|
||||
},
|
||||
TreeNode,
|
||||
methods: {
|
||||
updataTreeData (treeData) {
|
||||
const { $slots, $scopedSlots } = this
|
||||
return treeData.map((item) => {
|
||||
const { children, on = {}, slots = {}, scopedSlots = {}, key, class: cls, style, ...restProps } = item
|
||||
const treeNodeProps = {
|
||||
...restProps,
|
||||
icon: restProps.icon ||
|
||||
$slots[slots.icon] ||
|
||||
($scopedSlots[scopedSlots.icon] && $scopedSlots[scopedSlots.icon]),
|
||||
title: restProps.title ||
|
||||
$slots[slots.title] ||
|
||||
($scopedSlots[scopedSlots.title] && $scopedSlots[scopedSlots.title])(item),
|
||||
dataRef: item,
|
||||
on,
|
||||
key,
|
||||
class: cls,
|
||||
style,
|
||||
}
|
||||
if (children) {
|
||||
return { ...treeNodeProps, children: this.updataTreeData(children) }
|
||||
}
|
||||
return treeNodeProps
|
||||
})
|
||||
},
|
||||
},
|
||||
render () {
|
||||
const props = getOptionProps(this)
|
||||
const { prefixCls, showIcon, treeNodes } = props
|
||||
const checkable = props.checkable
|
||||
let treeData = props.treeData || treeNodes
|
||||
if (treeData) {
|
||||
treeData = this.updataTreeData(treeData)
|
||||
}
|
||||
const vcTreeProps = {
|
||||
props: {
|
||||
...props,
|
||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||
children: this.$slots.default || [],
|
||||
__propsSymbol__: Symbol(),
|
||||
},
|
||||
on: {
|
||||
...this.$listeners,
|
||||
},
|
||||
class: !showIcon && `${prefixCls}-icon-hide`,
|
||||
}
|
||||
if (treeData) {
|
||||
vcTreeProps.props.treeData = treeData
|
||||
}
|
||||
return (
|
||||
<VcTree {...vcTreeProps} />
|
||||
)
|
||||
},
|
||||
}
|
|
@ -18,7 +18,7 @@ basic controlled example
|
|||
v-model="checkedKeys"
|
||||
@select="onSelect"
|
||||
:selectedKeys="selectedKeys"
|
||||
:treeNodes="treeData"
|
||||
:treeData="treeData"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
|
@ -75,7 +75,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
onExpand (expandedKeys) {
|
||||
console.log('onExpand', arguments)
|
||||
console.log('onExpand', expandedKeys)
|
||||
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
|
||||
// or, you can remove all expanded children keys.
|
||||
this.expandedKeys = expandedKeys
|
||||
|
|
|
@ -12,7 +12,7 @@ The most basic usage, tell you how to use checkable, selectable, disabled, defau
|
|||
<template>
|
||||
<a-tree
|
||||
checkable
|
||||
:treeNodes="treeData"
|
||||
:treeData="treeData"
|
||||
:defaultExpandedKeys="['0-0-0', '0-0-1']"
|
||||
:defaultSelectedKeys="['0-0-0', '0-0-1']"
|
||||
:defaultCheckedKeys="['0-0-0', '0-0-1']"
|
||||
|
|
|
@ -11,7 +11,7 @@ You can customize icons for different nodes.
|
|||
```html
|
||||
<template>
|
||||
<a-tree
|
||||
:treeNodes="treeData"
|
||||
:treeData="treeData"
|
||||
showIcon
|
||||
defaultExpandAll
|
||||
:defaultSelectedKeys="['0-0-0']"
|
||||
|
@ -32,7 +32,7 @@ const treeData = [{
|
|||
},
|
||||
children: [
|
||||
{ title: 'leaf', key: '0-0-0', slots: { icon: 'meh' }},
|
||||
{ title: 'leaf', key: '0-1-1', scopedSlots: { icon: 'custom' }}],
|
||||
{ title: 'leaf', key: '0-0-1', scopedSlots: { icon: 'custom' }}],
|
||||
}]
|
||||
|
||||
export default {
|
||||
|
|
|
@ -16,7 +16,7 @@ Drag treeNode to insert after the other treeNode or insert into the other parent
|
|||
draggable
|
||||
@dragenter="onDragEnter"
|
||||
@drop="onDrop"
|
||||
:treeNodes="gData"
|
||||
:treeData="gData"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ To load data asynchronously when click to expand a treeNode.
|
|||
<template>
|
||||
<a-tree
|
||||
:loadData="onLoadData"
|
||||
:treeNodes="treeData"
|
||||
:treeData="treeData"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,183 +1,17 @@
|
|||
|
||||
import VcTree, { TreeNode } from '../vc-tree'
|
||||
import animation from '../_util/openAnimation'
|
||||
import PropTypes from '../_util/vue-types'
|
||||
import { initDefaultProps, getOptionProps } from '../_util/props-util'
|
||||
|
||||
// export interface AntTreeNodeProps {
|
||||
// disabled: PropTypes.bool,
|
||||
// disableCheckbox: PropTypes.bool,
|
||||
// title?: string | React.ReactNode;
|
||||
// key?: string;
|
||||
// isLeaf: PropTypes.bool,
|
||||
// children?: React.ReactNode;
|
||||
// }
|
||||
|
||||
// export interface AntTreeNode extends React.Component<AntTreeNodeProps, {}> {}
|
||||
|
||||
// export interface AntTreeNodeEvent {
|
||||
// event: 'check' | 'select';
|
||||
// node: AntTreeNode;
|
||||
// checked: PropTypes.bool,
|
||||
// checkedNodes?: Array<AntTreeNode>;
|
||||
// selected: PropTypes.bool,
|
||||
// selectedNodes?: Array<AntTreeNode>;
|
||||
// }
|
||||
|
||||
// export interface AntTreeNodeMouseEvent {
|
||||
// node: AntTreeNode;
|
||||
// event: React.MouseEventHandler<any>;
|
||||
// }
|
||||
|
||||
export const TreeProps = () => ({
|
||||
treeNodes: PropTypes.array,
|
||||
showLine: PropTypes.bool,
|
||||
/** 是否支持多选 */
|
||||
multiple: PropTypes.boolean,
|
||||
/** 是否自动展开父节点 */
|
||||
autoExpandParent: PropTypes.boolean,
|
||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||
checkStrictly: PropTypes.bool,
|
||||
/** 是否支持选中 */
|
||||
checkable: PropTypes.bool,
|
||||
/** 默认展开所有树节点 */
|
||||
defaultExpandAll: PropTypes.bool,
|
||||
/** 默认展开指定的树节点 */
|
||||
defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** (受控)展开指定的树节点 */
|
||||
expandedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** (受控)选中复选框的树节点 */
|
||||
checkedKeys: PropTypes.oneOfType(
|
||||
[
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
PropTypes.shape({
|
||||
checked: PropTypes.arrayOf(String),
|
||||
halfChecked: PropTypes.arrayOf(String),
|
||||
}).loose,
|
||||
]
|
||||
),
|
||||
/** 默认选中复选框的树节点 */
|
||||
defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** (受控)设置选中的树节点 */
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** 默认选中的树节点 */
|
||||
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** 展开/收起节点时触发 */
|
||||
// onExpand?: (expandedKeys: Array<string>, info: { node: AntTreeNode, expanded: boolean }) => void | PromiseLike<any>;
|
||||
/** 点击复选框触发 */
|
||||
// onCheck?: (checkedKeys: Array<string>, e: AntTreeNodeEvent) => void;
|
||||
/** 点击树节点触发 */
|
||||
// onSelect?: (selectedKeys: Array<string>, e: AntTreeNodeEvent) => void;
|
||||
/** filter some AntTreeNodes as you need. it should return true */
|
||||
filterAntTreeNode: PropTypes.func,
|
||||
/** 异步加载数据 */
|
||||
loadData: PropTypes.func,
|
||||
/** 响应右键点击 */
|
||||
// onRightClick?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** 设置节点可拖拽(IE>8)*/
|
||||
draggable: PropTypes.bool,
|
||||
// /** 开始拖拽时调用 */
|
||||
// onDragStart?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** dragenter 触发时调用 */
|
||||
// onDragEnter?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** dragover 触发时调用 */
|
||||
// onDragOver?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** dragleave 触发时调用 */
|
||||
// onDragLeave?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** drop 触发时调用 */
|
||||
// onDrop?: (options: AntTreeNodeMouseEvent) => void;
|
||||
prefixCls: PropTypes.string,
|
||||
filterTreeNode: PropTypes.func,
|
||||
showIcon: PropTypes.bool,
|
||||
openAnimation: PropTypes.any,
|
||||
})
|
||||
|
||||
const Tree = {
|
||||
name: 'ATree',
|
||||
TreeNode: { ...TreeNode, name: 'ATreeNode' },
|
||||
props: initDefaultProps(TreeProps(), {
|
||||
prefixCls: 'ant-tree',
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
openAnimation: animation,
|
||||
}),
|
||||
model: {
|
||||
prop: 'checkedKeys',
|
||||
event: 'check',
|
||||
},
|
||||
methods: {
|
||||
handleCheck (checkedKeys, e) {
|
||||
this.$emit('check', checkedKeys, e)
|
||||
},
|
||||
handelSelect (selectedKeys, e) {
|
||||
this.$emit('select', selectedKeys, e)
|
||||
this.$emit('update:select', selectedKeys)
|
||||
},
|
||||
handleExpand (expandedKeys, info) {
|
||||
this.$emit('expand', expandedKeys, info)
|
||||
this.$emit('update:expand', expandedKeys)
|
||||
},
|
||||
renderTreeNodes (data = []) {
|
||||
const { $slots, $scopedSlots } = this
|
||||
return data.map((item) => {
|
||||
const { children, on = {}, slots = {}, scopedSlots = {}, key, class: cls, style, ...restProps } = item
|
||||
const treeNodeProps = {
|
||||
props: {
|
||||
...restProps,
|
||||
icon: restProps.icon ||
|
||||
$slots[slots.icon] ||
|
||||
($scopedSlots[scopedSlots.icon] && $scopedSlots[scopedSlots.icon]),
|
||||
title: restProps.title ||
|
||||
$slots[slots.title] ||
|
||||
($scopedSlots[scopedSlots.title] && $scopedSlots[scopedSlots.title])(item),
|
||||
dataRef: item,
|
||||
},
|
||||
on,
|
||||
key,
|
||||
class: cls,
|
||||
style,
|
||||
}
|
||||
if (children) {
|
||||
return (
|
||||
<TreeNode {...treeNodeProps}>
|
||||
{this.renderTreeNodes(children)}
|
||||
</TreeNode>
|
||||
)
|
||||
}
|
||||
return <TreeNode {...treeNodeProps} />
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const props = getOptionProps(this)
|
||||
const { prefixCls, checkable, treeNodes, ...restProps } = props
|
||||
const { handelSelect, handleCheck, handleExpand, renderTreeNodes } = this
|
||||
const vcTreeProps = {
|
||||
props: {
|
||||
...restProps,
|
||||
prefixCls,
|
||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||
},
|
||||
on: {
|
||||
...this.$listeners,
|
||||
check: handleCheck,
|
||||
select: handelSelect,
|
||||
expand: handleExpand,
|
||||
},
|
||||
}
|
||||
return (
|
||||
<VcTree {...vcTreeProps}>
|
||||
{treeNodes ? renderTreeNodes(treeNodes) : this.$slots.default}
|
||||
</VcTree>
|
||||
)
|
||||
},
|
||||
}
|
||||
import Tree from './Tree'
|
||||
import DirectoryTree from './DirectoryTree'
|
||||
// export {
|
||||
// TreeProps,
|
||||
// } from './Tree'
|
||||
|
||||
Tree.TreeNode.name = 'ATreeNode'
|
||||
Tree.DirectoryTree = DirectoryTree
|
||||
/* istanbul ignore next */
|
||||
Tree.install = function (Vue) {
|
||||
Vue.component(Tree.name, Tree)
|
||||
Vue.component(Tree.TreeNode.name, Tree.TreeNode)
|
||||
Vue.component(DirectoryTree.name, DirectoryTree)
|
||||
}
|
||||
|
||||
export default Tree
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
|
||||
import VcTree, { TreeNode } from '../vc-tree'
|
||||
import animation from '../_util/openAnimation'
|
||||
import PropTypes from '../_util/vue-types'
|
||||
import { initDefaultProps, getOptionProps } from '../_util/props-util'
|
||||
|
||||
// export interface AntTreeNodeProps {
|
||||
// disabled: PropTypes.bool,
|
||||
// disableCheckbox: PropTypes.bool,
|
||||
// title?: string | React.ReactNode;
|
||||
// key?: string;
|
||||
// isLeaf: PropTypes.bool,
|
||||
// children?: React.ReactNode;
|
||||
// }
|
||||
|
||||
// export interface AntTreeNode extends React.Component<AntTreeNodeProps, {}> {}
|
||||
|
||||
// export interface AntTreeNodeEvent {
|
||||
// event: 'check' | 'select';
|
||||
// node: AntTreeNode;
|
||||
// checked: PropTypes.bool,
|
||||
// checkedNodes?: Array<AntTreeNode>;
|
||||
// selected: PropTypes.bool,
|
||||
// selectedNodes?: Array<AntTreeNode>;
|
||||
// }
|
||||
|
||||
// export interface AntTreeNodeMouseEvent {
|
||||
// node: AntTreeNode;
|
||||
// event: React.MouseEventHandler<any>;
|
||||
// }
|
||||
|
||||
export const TreeProps = () => ({
|
||||
treeNodes: PropTypes.array,
|
||||
showLine: PropTypes.bool,
|
||||
/** 是否支持多选 */
|
||||
multiple: PropTypes.boolean,
|
||||
/** 是否自动展开父节点 */
|
||||
autoExpandParent: PropTypes.boolean,
|
||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||
checkStrictly: PropTypes.bool,
|
||||
/** 是否支持选中 */
|
||||
checkable: PropTypes.bool,
|
||||
/** 默认展开所有树节点 */
|
||||
defaultExpandAll: PropTypes.bool,
|
||||
/** 默认展开指定的树节点 */
|
||||
defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** (受控)展开指定的树节点 */
|
||||
expandedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** (受控)选中复选框的树节点 */
|
||||
checkedKeys: PropTypes.oneOfType(
|
||||
[
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
PropTypes.shape({
|
||||
checked: PropTypes.arrayOf(String),
|
||||
halfChecked: PropTypes.arrayOf(String),
|
||||
}).loose,
|
||||
]
|
||||
),
|
||||
/** 默认选中复选框的树节点 */
|
||||
defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** (受控)设置选中的树节点 */
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** 默认选中的树节点 */
|
||||
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
/** 展开/收起节点时触发 */
|
||||
// onExpand?: (expandedKeys: Array<string>, info: { node: AntTreeNode, expanded: boolean }) => void | PromiseLike<any>;
|
||||
/** 点击复选框触发 */
|
||||
// onCheck?: (checkedKeys: Array<string>, e: AntTreeNodeEvent) => void;
|
||||
/** 点击树节点触发 */
|
||||
// onSelect?: (selectedKeys: Array<string>, e: AntTreeNodeEvent) => void;
|
||||
/** filter some AntTreeNodes as you need. it should return true */
|
||||
filterAntTreeNode: PropTypes.func,
|
||||
/** 异步加载数据 */
|
||||
loadData: PropTypes.func,
|
||||
/** 响应右键点击 */
|
||||
// onRightClick?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** 设置节点可拖拽(IE>8)*/
|
||||
draggable: PropTypes.bool,
|
||||
// /** 开始拖拽时调用 */
|
||||
// onDragStart?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** dragenter 触发时调用 */
|
||||
// onDragEnter?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** dragover 触发时调用 */
|
||||
// onDragOver?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** dragleave 触发时调用 */
|
||||
// onDragLeave?: (options: AntTreeNodeMouseEvent) => void;
|
||||
// /** drop 触发时调用 */
|
||||
// onDrop?: (options: AntTreeNodeMouseEvent) => void;
|
||||
prefixCls: PropTypes.string,
|
||||
filterTreeNode: PropTypes.func,
|
||||
showIcon: PropTypes.bool,
|
||||
openAnimation: PropTypes.any,
|
||||
})
|
||||
|
||||
const Tree = {
|
||||
name: 'ATree',
|
||||
TreeNode: { ...TreeNode, name: 'ATreeNode' },
|
||||
props: initDefaultProps(TreeProps(), {
|
||||
prefixCls: 'ant-tree',
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
openAnimation: animation,
|
||||
}),
|
||||
model: {
|
||||
prop: 'checkedKeys',
|
||||
event: 'check',
|
||||
},
|
||||
methods: {
|
||||
handleCheck (checkedKeys, e) {
|
||||
this.$emit('check', checkedKeys, e)
|
||||
},
|
||||
handelSelect (selectedKeys, e) {
|
||||
this.$emit('select', selectedKeys, e)
|
||||
this.$emit('update:select', selectedKeys)
|
||||
},
|
||||
handleExpand (expandedKeys, info) {
|
||||
this.$emit('expand', expandedKeys, info)
|
||||
this.$emit('update:expand', expandedKeys)
|
||||
},
|
||||
renderTreeNodes (data = []) {
|
||||
const { $slots, $scopedSlots } = this
|
||||
return data.map((item) => {
|
||||
const { children, on = {}, slots = {}, scopedSlots = {}, key, class: cls, style, ...restProps } = item
|
||||
const treeNodeProps = {
|
||||
props: {
|
||||
...restProps,
|
||||
icon: restProps.icon ||
|
||||
$slots[slots.icon] ||
|
||||
($scopedSlots[scopedSlots.icon] && $scopedSlots[scopedSlots.icon]),
|
||||
title: restProps.title ||
|
||||
$slots[slots.title] ||
|
||||
($scopedSlots[scopedSlots.title] && $scopedSlots[scopedSlots.title])(item),
|
||||
dataRef: item,
|
||||
},
|
||||
on,
|
||||
key,
|
||||
class: cls,
|
||||
style,
|
||||
}
|
||||
if (children) {
|
||||
return (
|
||||
<TreeNode {...treeNodeProps}>
|
||||
{this.renderTreeNodes(children)}
|
||||
</TreeNode>
|
||||
)
|
||||
}
|
||||
return <TreeNode {...treeNodeProps} />
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const props = getOptionProps(this)
|
||||
const { prefixCls, checkable, treeNodes, ...restProps } = props
|
||||
const { handelSelect, handleCheck, handleExpand, renderTreeNodes } = this
|
||||
const vcTreeProps = {
|
||||
props: {
|
||||
...restProps,
|
||||
prefixCls,
|
||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||
},
|
||||
on: {
|
||||
...this.$listeners,
|
||||
check: handleCheck,
|
||||
select: handelSelect,
|
||||
expand: handleExpand,
|
||||
},
|
||||
}
|
||||
return (
|
||||
<VcTree {...vcTreeProps}>
|
||||
{treeNodes ? renderTreeNodes(treeNodes) : this.$slots.default}
|
||||
</VcTree>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
Tree.install = function (Vue) {
|
||||
Vue.component(Tree.name, Tree)
|
||||
Vue.component(Tree.TreeNode.name, Tree.TreeNode)
|
||||
}
|
||||
|
||||
export default Tree
|
|
@ -0,0 +1,72 @@
|
|||
import { getNodeChildren, convertTreeToEntities } from '../vc-tree/src/util'
|
||||
|
||||
const Record = {
|
||||
None: 'node',
|
||||
Start: 'start',
|
||||
End: 'end',
|
||||
}
|
||||
|
||||
// TODO: Move this logic into `rc-tree`
|
||||
function traverseNodesKey (rootChildren, callback) {
|
||||
const nodeList = getNodeChildren(rootChildren) || []
|
||||
|
||||
function processNode (node) {
|
||||
const { key, props: { children }} = node
|
||||
if (callback(key) !== false) {
|
||||
traverseNodesKey(children, callback)
|
||||
}
|
||||
}
|
||||
|
||||
nodeList.forEach(processNode)
|
||||
}
|
||||
|
||||
export function getFullKeyList (children) {
|
||||
const { keyEntities } = convertTreeToEntities(children)
|
||||
return Object.keys(keyEntities)
|
||||
}
|
||||
|
||||
/** 计算选中范围,只考虑expanded情况以优化性能 */
|
||||
export function calcRangeKeys (rootChildren, expandedKeys, startKey, endKey) {
|
||||
const keys = []
|
||||
let record = Record.None
|
||||
|
||||
if (startKey && startKey === endKey) {
|
||||
return [startKey]
|
||||
}
|
||||
if (!startKey || !endKey) {
|
||||
return []
|
||||
}
|
||||
|
||||
function matchKey (key) {
|
||||
return key === startKey || key === endKey
|
||||
}
|
||||
|
||||
traverseNodesKey(rootChildren, (key) => {
|
||||
if (record === Record.End) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (matchKey(key)) {
|
||||
// Match test
|
||||
keys.push(key)
|
||||
|
||||
if (record === Record.None) {
|
||||
record = Record.Start
|
||||
} else if (record === Record.Start) {
|
||||
record = Record.End
|
||||
return false
|
||||
}
|
||||
} else if (record === Record.Start) {
|
||||
// Append selection
|
||||
keys.push(key)
|
||||
}
|
||||
|
||||
if (expandedKeys.indexOf(key) === -1) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return keys
|
||||
}
|
|
@ -161,7 +161,7 @@
|
|||
>span:not(.@{treePrefixCls}-switcher),
|
||||
>a,
|
||||
>a span {
|
||||
color: #ccc;
|
||||
color: #767676;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
/* eslint no-console:0 */
|
||||
import Tree, { TreeNode } from '../index'
|
||||
import '../assets/index.less'
|
||||
import { gData,
|
||||
/* filterParentPosition, getFilterExpandedKeys,*/ getRadioSelectKeys } from './util'
|
||||
import { gData, getRadioSelectKeys } from './util'
|
||||
import '../../vc-dialog/assets/index.less'
|
||||
import Modal from '../../vc-dialog'
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
|
@ -63,7 +62,7 @@ export default {
|
|||
onRbSelect (selectedKeys, info) {
|
||||
let _selectedKeys = selectedKeys
|
||||
if (info.selected) {
|
||||
_selectedKeys = getRadioSelectKeys(gData, selectedKeys, info.node.props.eventKey)
|
||||
_selectedKeys = getRadioSelectKeys(gData, selectedKeys, info.node.eventKey)
|
||||
}
|
||||
this.setState({
|
||||
selectedKeys: _selectedKeys,
|
||||
|
|
|
@ -5,6 +5,24 @@ import Tree, { TreeNode } from '../index'
|
|||
import '../assets/index.less'
|
||||
import './basic.less'
|
||||
|
||||
const treeData = [
|
||||
{ key: '0-0', title: 'parent 1', children:
|
||||
[
|
||||
{ key: '0-0-0', title: 'parent 1-1', children:
|
||||
[
|
||||
{ key: '0-0-0-0', title: 'parent 1-1-0' },
|
||||
],
|
||||
},
|
||||
{ key: '0-0-1', title: 'parent 1-2', children:
|
||||
[
|
||||
{ key: '0-0-1-0', title: 'parent 1-2-0', disableCheckbox: true },
|
||||
{ key: '0-0-1-1', title: 'parent 1-2-1' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default {
|
||||
props: {
|
||||
keys: PropTypes.array.def(['0-0-0-0']),
|
||||
|
@ -15,7 +33,6 @@ export default {
|
|||
defaultExpandedKeys: keys,
|
||||
defaultSelectedKeys: keys,
|
||||
defaultCheckedKeys: keys,
|
||||
switchIt: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -46,15 +63,20 @@ export default {
|
|||
},
|
||||
|
||||
render () {
|
||||
const customLabel = (<span class='cus-label'>
|
||||
<span>operations: </span>
|
||||
<span style={{ color: 'blue' }} onClick={this.onEdit}>Edit</span>
|
||||
<label onClick={(e) => e.stopPropagation()}><input type='checkbox' /> checked</label>
|
||||
<span style={{ color: 'red' }} onClick={this.onDel}>Delete</span>
|
||||
</span>)
|
||||
const customLabel = (
|
||||
<span class='cus-label'>
|
||||
<span>operations: </span>
|
||||
<span style={{ color: 'blue' }} onClick={this.onEdit}>Edit</span>
|
||||
<label onClick={(e) => e.stopPropagation()}>
|
||||
<input type='checkbox' /> checked
|
||||
</label>
|
||||
|
||||
<span style={{ color: '#EB0000' }} onClick={this.onDel}>Delete</span>
|
||||
</span>
|
||||
)
|
||||
return (<div style={{ margin: '0 20px' }}>
|
||||
<h2>simple</h2>
|
||||
<Tree
|
||||
{/* <Tree
|
||||
class='myCls' showLine checkable defaultExpandAll
|
||||
defaultExpandedKeys={this.defaultExpandedKeys}
|
||||
onExpand={this.onExpand}
|
||||
|
@ -76,7 +98,7 @@ export default {
|
|||
<TreeNode title='parent 1-2-1' key='0-0-2-1' />
|
||||
</TreeNode>
|
||||
</TreeNode>
|
||||
</Tree>
|
||||
</Tree> */}
|
||||
|
||||
<h2>Check on Click TreeNode</h2>
|
||||
<Tree
|
||||
|
@ -90,17 +112,8 @@ export default {
|
|||
defaultCheckedKeys={this.defaultCheckedKeys}
|
||||
onSelect={this.onSelect}
|
||||
onCheck={this.onCheck}
|
||||
>
|
||||
<TreeNode title='parent 1' key='0-0'>
|
||||
<TreeNode title='parent 1-1' key='0-0-0'>
|
||||
<TreeNode title='parent 1-1-0' key='0-0-0-0' />
|
||||
</TreeNode>
|
||||
<TreeNode title='parent 1-2' key='0-0-1'>
|
||||
<TreeNode title='parent 1-2-0' key='0-0-1-0' disableCheckbox />
|
||||
<TreeNode title='parent 1-2-1' key='0-0-1-1' />
|
||||
</TreeNode>
|
||||
</TreeNode>
|
||||
</Tree>
|
||||
treeData={treeData}
|
||||
/>
|
||||
</div>)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,12 +4,16 @@ import warning from 'warning'
|
|||
import { initDefaultProps, getOptionProps, getSlots } from '../../_util/props-util'
|
||||
import { cloneElement } from '../../_util/vnode'
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import proxyComponent from '../../_util/proxyComponent'
|
||||
import {
|
||||
traverseTreeNodes, getStrictlyValue,
|
||||
getFullKeyList, getPosition, getDragNodesKeys,
|
||||
calcExpandedKeys, calcSelectedKeys,
|
||||
calcCheckedKeys, calcDropPosition,
|
||||
arrAdd, arrDel, posToArr, mapChildren,
|
||||
convertTreeToEntities, convertDataToTree,
|
||||
getPosition, getDragNodesKeys,
|
||||
parseCheckedKeys,
|
||||
conductExpandParent, calcSelectedKeys,
|
||||
calcDropPosition,
|
||||
arrAdd, arrDel, posToArr,
|
||||
mapChildren, conductCheck,
|
||||
warnOnlyTreeNode,
|
||||
} from './util'
|
||||
|
||||
/**
|
||||
|
@ -22,6 +26,9 @@ const Tree = {
|
|||
mixins: [BaseMixin],
|
||||
props: initDefaultProps({
|
||||
prefixCls: PropTypes.string,
|
||||
tabIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
children: PropTypes.any,
|
||||
treeData: PropTypes.array, // Generate treeNode by children
|
||||
showLine: PropTypes.bool,
|
||||
showIcon: PropTypes.bool,
|
||||
icon: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
||||
|
@ -63,7 +70,8 @@ const Tree = {
|
|||
filterTreeNode: PropTypes.func,
|
||||
openTransitionName: PropTypes.string,
|
||||
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
children: PropTypes.any,
|
||||
switcherIcon: PropTypes.any,
|
||||
_propsSymbol: PropTypes.any,
|
||||
}, {
|
||||
prefixCls: 'rc-tree',
|
||||
showLine: false,
|
||||
|
@ -82,53 +90,31 @@ const Tree = {
|
|||
defaultSelectedKeys: [],
|
||||
}),
|
||||
|
||||
// static childContextTypes = contextTypes;
|
||||
|
||||
data () {
|
||||
const props = getOptionProps(this)
|
||||
const {
|
||||
defaultExpandAll,
|
||||
defaultExpandParent,
|
||||
defaultExpandedKeys,
|
||||
defaultCheckedKeys,
|
||||
defaultSelectedKeys,
|
||||
expandedKeys,
|
||||
} = props
|
||||
const children = this.$slots.default
|
||||
// Sync state with props
|
||||
const { checkedKeys = [], halfCheckedKeys = [] } =
|
||||
calcCheckedKeys(defaultCheckedKeys, props, children) || {}
|
||||
|
||||
const state = {
|
||||
sSelectedKeys: calcSelectedKeys(defaultSelectedKeys, props),
|
||||
sCheckedKeys: checkedKeys,
|
||||
sHalfCheckedKeys: halfCheckedKeys,
|
||||
}
|
||||
|
||||
if (defaultExpandAll) {
|
||||
state.sExpandedKeys = getFullKeyList(children)
|
||||
} else if (defaultExpandParent) {
|
||||
state.sExpandedKeys = calcExpandedKeys(expandedKeys || defaultExpandedKeys, props, children)
|
||||
} else {
|
||||
state.sExpandedKeys = defaultExpandedKeys
|
||||
}
|
||||
|
||||
// Cache for check status to optimize
|
||||
this.checkedBatch = null
|
||||
this.propsToStateMap = {
|
||||
expandedKeys: 'sExpandedKeys',
|
||||
selectedKeys: 'sSelectedKeys',
|
||||
checkedKeys: 'sCheckedKeys',
|
||||
halfCheckedKeys: 'sHalfCheckedKeys',
|
||||
_posEntities: {},
|
||||
_keyEntities: {},
|
||||
_expandedKeys: [],
|
||||
_selectedKeys: [],
|
||||
_checkedKeys: [],
|
||||
_halfCheckedKeys: [],
|
||||
_loadedKeys: [],
|
||||
_loadingKeys: [],
|
||||
_treeNode: [],
|
||||
_prevProps: null,
|
||||
_dragOverNodeKey: '',
|
||||
_dropPosition: null,
|
||||
_dragNodesKeys: [],
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
...this.getSyncProps(props),
|
||||
dragOverNodeKey: '',
|
||||
dropPosition: null,
|
||||
dragNodesKeys: [],
|
||||
sLoadedKeys: [],
|
||||
sLoadingKeys: [],
|
||||
// ...this.getSyncProps(props),
|
||||
// dragOverNodeKey: '',
|
||||
// dropPosition: null,
|
||||
// dragNodesKeys: [],
|
||||
// sLoadedKeys: [],
|
||||
// sLoadingKeys: [],
|
||||
...this.getDerivedStateFromProps(getOptionProps(this), state),
|
||||
}
|
||||
},
|
||||
provide () {
|
||||
|
@ -138,45 +124,110 @@ const Tree = {
|
|||
},
|
||||
|
||||
watch: {
|
||||
children (val) {
|
||||
const { checkedKeys = [], halfCheckedKeys = [] } = calcCheckedKeys(this.checkedKeys || this.sCheckedKeys, this.$props, val) || {}
|
||||
this.sCheckedKeys = checkedKeys
|
||||
this.sHalfCheckedKeys = halfCheckedKeys
|
||||
},
|
||||
autoExpandParent (val) {
|
||||
this.sExpandedKeys = val ? calcExpandedKeys(this.expandedKeys, this.$props, this.$slots.default) : this.expandedKeys
|
||||
},
|
||||
expandedKeys (val) {
|
||||
this.sExpandedKeys = this.autoExpandParent ? calcExpandedKeys(val, this.$props, this.$slots.default) : val
|
||||
},
|
||||
selectedKeys (val) {
|
||||
this.sSelectedKeys = calcSelectedKeys(val, this.$props, this.$slots.default)
|
||||
},
|
||||
checkedKeys (val) {
|
||||
const { checkedKeys = [], halfCheckedKeys = [] } = calcCheckedKeys(val, this.$props, this.$slots.default) || {}
|
||||
this.sCheckedKeys = checkedKeys
|
||||
this.sHalfCheckedKeys = halfCheckedKeys
|
||||
},
|
||||
loadedKeys (val) {
|
||||
this.sLoadedKeys = val
|
||||
__propsSymbol__ () {
|
||||
this.setState(this.getDerivedStateFromProps(getOptionProps(this), this.$data))
|
||||
},
|
||||
},
|
||||
|
||||
// componentWillReceiveProps (nextProps) {
|
||||
// // React 16 will not trigger update if new state is null
|
||||
// this.setState(this.getSyncProps(nextProps, this.props))
|
||||
// },
|
||||
|
||||
methods: {
|
||||
getDerivedStateFromProps (props, prevState) {
|
||||
const { _prevProps } = prevState
|
||||
const newState = {
|
||||
_prevProps: { ...props },
|
||||
}
|
||||
|
||||
function needSync (name) {
|
||||
return (!_prevProps && name in props) || (_prevProps && _prevProps[name] !== props[name])
|
||||
}
|
||||
|
||||
// ================== Tree Node ==================
|
||||
let treeNode = null
|
||||
|
||||
// Check if `treeData` or `children` changed and save into the state.
|
||||
if (needSync('treeData')) {
|
||||
treeNode = convertDataToTree(this.$createElement, props.treeData)
|
||||
} else if (needSync('children')) {
|
||||
treeNode = props.children
|
||||
}
|
||||
|
||||
// Tree support filter function which will break the tree structure in the vdm.
|
||||
// We cache the treeNodes in state so that we can return the treeNode in event trigger.
|
||||
if (treeNode) {
|
||||
newState._treeNode = treeNode
|
||||
|
||||
// Calculate the entities data for quick match
|
||||
const entitiesMap = convertTreeToEntities(treeNode)
|
||||
newState._posEntities = entitiesMap.posEntities
|
||||
newState._keyEntities = entitiesMap.keyEntities
|
||||
}
|
||||
|
||||
const keyEntities = newState._keyEntities || prevState._keyEntities
|
||||
|
||||
// ================ expandedKeys =================
|
||||
if (needSync('expandedKeys') || (_prevProps && needSync('autoExpandParent'))) {
|
||||
newState._expandedKeys = (props.autoExpandParent || (!_prevProps && props.defaultExpandParent))
|
||||
? conductExpandParent(props.expandedKeys, keyEntities) : props.expandedKeys
|
||||
} else if (!_prevProps && props.defaultExpandAll) {
|
||||
newState._expandedKeys = Object.keys(keyEntities)
|
||||
} else if (!_prevProps && props.defaultExpandedKeys) {
|
||||
newState._expandedKeys = (props.autoExpandParent || props.defaultExpandParent)
|
||||
? conductExpandParent(props.defaultExpandedKeys, keyEntities) : props.defaultExpandedKeys
|
||||
}
|
||||
|
||||
// ================ selectedKeys =================
|
||||
if (props.selectable) {
|
||||
if (needSync('selectedKeys')) {
|
||||
newState._selectedKeys = calcSelectedKeys(props.selectedKeys, props)
|
||||
} else if (!_prevProps && props.defaultSelectedKeys) {
|
||||
newState._selectedKeys = calcSelectedKeys(props.defaultSelectedKeys, props)
|
||||
}
|
||||
}
|
||||
|
||||
// ================= checkedKeys =================
|
||||
if (props.checkable) {
|
||||
let checkedKeyEntity
|
||||
|
||||
if (needSync('checkedKeys')) {
|
||||
checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {}
|
||||
} else if (!_prevProps && props.defaultCheckedKeys) {
|
||||
checkedKeyEntity = parseCheckedKeys(props.defaultCheckedKeys) || {}
|
||||
} else if (treeNode) {
|
||||
// If treeNode changed, we also need check it
|
||||
checkedKeyEntity = {
|
||||
checkedKeys: prevState._checkedKeys,
|
||||
halfCheckedKeys: prevState._halfCheckedKeys,
|
||||
}
|
||||
}
|
||||
|
||||
if (checkedKeyEntity) {
|
||||
let { checkedKeys = [], halfCheckedKeys = [] } = checkedKeyEntity
|
||||
|
||||
if (!props.checkStrictly) {
|
||||
const conductKeys = conductCheck(checkedKeys, true, keyEntities)
|
||||
checkedKeys = conductKeys.checkedKeys
|
||||
halfCheckedKeys = conductKeys.halfCheckedKeys
|
||||
}
|
||||
|
||||
newState._checkedKeys = checkedKeys
|
||||
newState._halfCheckedKeys = halfCheckedKeys
|
||||
}
|
||||
}
|
||||
// ================= loadedKeys ==================
|
||||
if (needSync('loadedKeys')) {
|
||||
newState._loadedKeys = props.loadedKeys
|
||||
}
|
||||
|
||||
return newState
|
||||
},
|
||||
onNodeDragStart (event, node) {
|
||||
const { sExpandedKeys } = this
|
||||
const { _expandedKeys } = this.$data
|
||||
const { eventKey } = node
|
||||
const children = getSlots(node).default
|
||||
this.dragNode = node
|
||||
|
||||
this.setState({
|
||||
dragNodesKeys: getDragNodesKeys(children, node),
|
||||
sExpandedKeys: arrDel(sExpandedKeys, eventKey),
|
||||
_dragNodesKeys: getDragNodesKeys(children, node),
|
||||
_expandedKeys: arrDel(_expandedKeys, eventKey),
|
||||
})
|
||||
this.__emit('dragstart', { event, node })
|
||||
},
|
||||
|
@ -189,10 +240,10 @@ const Tree = {
|
|||
* But let's just keep it to avoid event trigger logic change.
|
||||
*/
|
||||
onNodeDragEnter (event, node) {
|
||||
const { sExpandedKeys } = this
|
||||
const { _expandedKeys: expandedKeys } = this.$data
|
||||
const { pos, eventKey } = node
|
||||
|
||||
if (!this.dragNode) return
|
||||
if (!this.dragNode || !node.$refs.selectHandle) return
|
||||
|
||||
const dropPosition = calcDropPosition(event, node)
|
||||
|
||||
|
@ -202,8 +253,8 @@ const Tree = {
|
|||
dropPosition === 0
|
||||
) {
|
||||
this.setState({
|
||||
dragOverNodeKey: '',
|
||||
dropPosition: null,
|
||||
_dragOverNodeKey: '',
|
||||
_dropPosition: null,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
@ -216,8 +267,8 @@ const Tree = {
|
|||
setTimeout(() => {
|
||||
// Update drag over node
|
||||
this.setState({
|
||||
dragOverNodeKey: eventKey,
|
||||
dropPosition,
|
||||
_dragOverNodeKey: eventKey,
|
||||
_dropPosition: dropPosition,
|
||||
})
|
||||
|
||||
// Side effect for delay drag
|
||||
|
@ -228,9 +279,9 @@ const Tree = {
|
|||
clearTimeout(this.delayedDragEnterLogic[key])
|
||||
})
|
||||
this.delayedDragEnterLogic[pos] = setTimeout(() => {
|
||||
const newExpandedKeys = arrAdd(sExpandedKeys, eventKey)
|
||||
const newExpandedKeys = arrAdd(expandedKeys, eventKey)
|
||||
this.setState({
|
||||
sExpandedKeys: newExpandedKeys,
|
||||
_expandedKeys: newExpandedKeys,
|
||||
})
|
||||
this.__emit('dragenter', { event, node, expandedKeys: newExpandedKeys })
|
||||
}, 400)
|
||||
|
@ -238,42 +289,41 @@ const Tree = {
|
|||
},
|
||||
onNodeDragOver (event, node) {
|
||||
const { eventKey } = node
|
||||
|
||||
const { _dragOverNodeKey, _dropPosition } = this.$data
|
||||
// Update drag position
|
||||
if (this.dragNode && eventKey === this.dragOverNodeKey) {
|
||||
if (this.dragNode && eventKey === _dragOverNodeKey && node.$refs.selectHandle) {
|
||||
const dropPosition = calcDropPosition(event, node)
|
||||
|
||||
if (dropPosition === this.dropPosition) return
|
||||
if (dropPosition === _dropPosition) return
|
||||
|
||||
this.setState({
|
||||
dropPosition,
|
||||
_dropPosition,
|
||||
})
|
||||
}
|
||||
this.__emit('dragover', { event, node })
|
||||
},
|
||||
onNodeDragLeave (event, node) {
|
||||
this.setState({
|
||||
dragOverNodeKey: '',
|
||||
_dragOverNodeKey: '',
|
||||
})
|
||||
this.__emit('dragleave', { event, node })
|
||||
},
|
||||
onNodeDragEnd (event, node) {
|
||||
this.setState({
|
||||
dragOverNodeKey: '',
|
||||
_dragOverNodeKey: '',
|
||||
})
|
||||
this.__emit('dragend', { event, node })
|
||||
},
|
||||
onNodeDrop (event, node) {
|
||||
const { dragNodesKeys = [], dropPosition } = this
|
||||
const { _dragNodesKeys = [], _dropPosition } = this.$data
|
||||
|
||||
const { eventKey, pos } = node
|
||||
|
||||
this.setState({
|
||||
dragOverNodeKey: '',
|
||||
dropNodeKey: eventKey,
|
||||
_dragOverNodeKey: '',
|
||||
})
|
||||
|
||||
if (dragNodesKeys.indexOf(eventKey) !== -1) {
|
||||
if (_dragNodesKeys.indexOf(eventKey) !== -1) {
|
||||
warning(false, 'Can not drop to dragNode(include it\'s children node)')
|
||||
return
|
||||
}
|
||||
|
@ -284,11 +334,11 @@ const Tree = {
|
|||
event,
|
||||
node,
|
||||
dragNode: this.dragNode,
|
||||
dragNodesKeys: dragNodesKeys.slice(),
|
||||
dropPosition: dropPosition + Number(posArr[posArr.length - 1]),
|
||||
dragNodesKeys: _dragNodesKeys.slice(),
|
||||
dropPosition: _dropPosition + Number(posArr[posArr.length - 1]),
|
||||
}
|
||||
|
||||
if (dropPosition !== 0) {
|
||||
if (_dropPosition !== 0) {
|
||||
dropResult.dropToGap = true
|
||||
}
|
||||
this.__emit('drop', dropResult)
|
||||
|
@ -303,10 +353,11 @@ const Tree = {
|
|||
},
|
||||
|
||||
onNodeSelect (e, treeNode) {
|
||||
const { sSelectedKeys, multiple, $slots: { default: children }} = this
|
||||
let { _selectedKeys: selectedKeys } = this.$data
|
||||
const { _keyEntities: keyEntities } = this.$data
|
||||
const { multiple } = this.$props
|
||||
const { selected, eventKey } = getOptionProps(treeNode)
|
||||
const targetSelected = !selected
|
||||
let selectedKeys = sSelectedKeys
|
||||
// Update selected keys
|
||||
if (!targetSelected) {
|
||||
selectedKeys = arrDel(selectedKeys, eventKey)
|
||||
|
@ -317,17 +368,14 @@ const Tree = {
|
|||
}
|
||||
|
||||
// [Legacy] Not found related usage in doc or upper libs
|
||||
// [Legacy] TODO: add optimize prop to skip node process
|
||||
const selectedNodes = []
|
||||
if (selectedKeys.length) {
|
||||
traverseTreeNodes(children, ({ node, key }) => {
|
||||
if (selectedKeys.indexOf(key) !== -1) {
|
||||
selectedNodes.push(node)
|
||||
}
|
||||
})
|
||||
}
|
||||
const selectedNodes = selectedKeys.map(key => {
|
||||
const entity = keyEntities[key]
|
||||
if (!entity) return null
|
||||
|
||||
this.setUncontrolledState({ selectedKeys })
|
||||
return entity.node
|
||||
}).filter(node => node)
|
||||
|
||||
this.setUncontrolledState({ _selectedKeys: selectedKeys })
|
||||
|
||||
const eventObj = {
|
||||
event: 'select',
|
||||
|
@ -338,142 +386,100 @@ const Tree = {
|
|||
}
|
||||
this.__emit('select', selectedKeys, eventObj)
|
||||
},
|
||||
|
||||
onNodeLoad (treeNode) {
|
||||
const { loadData } = this.$props
|
||||
const { sLoadedKeys = [], sLoadingKeys = [] } = this.$data
|
||||
onNodeCheck (e, treeNode, checked) {
|
||||
const { _keyEntities: keyEntities, _checkedKeys: oriCheckedKeys, _halfCheckedKeys: oriHalfCheckedKeys } = this.$data
|
||||
const { checkStrictly } = this.$props
|
||||
const { eventKey } = getOptionProps(treeNode)
|
||||
|
||||
if (!loadData || sLoadedKeys.indexOf(eventKey) !== -1 || sLoadingKeys.indexOf(eventKey) !== -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
this.setState({
|
||||
sLoadingKeys: arrAdd(sLoadingKeys, eventKey),
|
||||
})
|
||||
const promise = loadData(treeNode)
|
||||
promise.then(() => {
|
||||
const newLoadedKeys = arrAdd(this.sLoadedKeys, eventKey)
|
||||
this.setUncontrolledState({
|
||||
sLoadedKeys: newLoadedKeys,
|
||||
})
|
||||
this.setState({
|
||||
sLoadingKeys: arrDel(this.sLoadingKeys, eventKey),
|
||||
})
|
||||
|
||||
const eventObj = {
|
||||
event: 'load',
|
||||
node: treeNode,
|
||||
}
|
||||
this.__emit('load', newLoadedKeys, eventObj)
|
||||
})
|
||||
|
||||
return promise
|
||||
},
|
||||
|
||||
/**
|
||||
* This will cache node check status to optimize update process.
|
||||
* When Tree get trigger `onCheckConductFinished` will flush all the update.
|
||||
*/
|
||||
onBatchNodeCheck (key, checked, halfChecked, startNode) {
|
||||
if (startNode) {
|
||||
this.checkedBatch = {
|
||||
treeNode: startNode,
|
||||
checked,
|
||||
list: [],
|
||||
}
|
||||
}
|
||||
|
||||
// This code should never called
|
||||
if (!this.checkedBatch) {
|
||||
this.checkedBatch = {
|
||||
list: [],
|
||||
}
|
||||
warning(
|
||||
false,
|
||||
'Checked batch not init. This should be a bug. Please fire a issue.'
|
||||
)
|
||||
}
|
||||
|
||||
this.checkedBatch.list.push({ key, checked, halfChecked })
|
||||
},
|
||||
|
||||
/**
|
||||
* When top `onCheckConductFinished` called, will execute all batch update.
|
||||
* And trigger `onCheck` event.
|
||||
*/
|
||||
onCheckConductFinished (e) {
|
||||
const { sCheckedKeys, sHalfCheckedKeys, checkStrictly, $slots: { default: children }} = this
|
||||
|
||||
// Use map to optimize update speed
|
||||
const checkedKeySet = {}
|
||||
const halfCheckedKeySet = {}
|
||||
|
||||
sCheckedKeys.forEach(key => {
|
||||
checkedKeySet[key] = true
|
||||
})
|
||||
sHalfCheckedKeys.forEach(key => {
|
||||
halfCheckedKeySet[key] = true
|
||||
})
|
||||
|
||||
// Batch process
|
||||
this.checkedBatch.list.forEach(({ key, checked, halfChecked }) => {
|
||||
checkedKeySet[key] = checked
|
||||
halfCheckedKeySet[key] = halfChecked
|
||||
})
|
||||
const newCheckedKeys = Object.keys(checkedKeySet).filter(key => checkedKeySet[key])
|
||||
const newHalfCheckedKeys = Object.keys(halfCheckedKeySet).filter(key => halfCheckedKeySet[key])
|
||||
|
||||
// Trigger onChecked
|
||||
let selectedObj
|
||||
|
||||
// Prepare trigger arguments
|
||||
let checkedObj
|
||||
const eventObj = {
|
||||
event: 'check',
|
||||
node: this.checkedBatch.treeNode,
|
||||
checked: this.checkedBatch.checked,
|
||||
node: treeNode,
|
||||
checked,
|
||||
nativeEvent: e.nativeEvent,
|
||||
}
|
||||
|
||||
if (checkStrictly) {
|
||||
selectedObj = getStrictlyValue(newCheckedKeys, newHalfCheckedKeys)
|
||||
const checkedKeys = checked ? arrAdd(oriCheckedKeys, eventKey) : arrDel(oriCheckedKeys, eventKey)
|
||||
const halfCheckedKeys = arrDel(oriHalfCheckedKeys, eventKey)
|
||||
checkedObj = { checked: checkedKeys, halfChecked: halfCheckedKeys }
|
||||
|
||||
// [Legacy] TODO: add optimize prop to skip node process
|
||||
eventObj.checkedNodes = []
|
||||
traverseTreeNodes(children, ({ node, key }) => {
|
||||
if (checkedKeySet[key]) {
|
||||
eventObj.checkedNodes.push(node)
|
||||
}
|
||||
eventObj.checkedNodes = checkedKeys
|
||||
.map(key => keyEntities[key])
|
||||
.filter(entity => entity)
|
||||
.map(entity => entity.node)
|
||||
|
||||
this.setUncontrolledState({ _checkedKeys: checkedKeys })
|
||||
} else {
|
||||
const { checkedKeys, halfCheckedKeys } = conductCheck([eventKey], checked, keyEntities, {
|
||||
checkedKeys: oriCheckedKeys, halfCheckedKeys: oriHalfCheckedKeys,
|
||||
})
|
||||
|
||||
this.setUncontrolledState({ checkedKeys: newCheckedKeys })
|
||||
} else {
|
||||
selectedObj = newCheckedKeys
|
||||
checkedObj = checkedKeys
|
||||
|
||||
// [Legacy] TODO: add optimize prop to skip node process
|
||||
// [Legacy] This is used for `rc-tree-select`
|
||||
eventObj.checkedNodes = []
|
||||
eventObj.checkedNodesPositions = [] // [Legacy] TODO: not in API
|
||||
eventObj.halfCheckedKeys = newHalfCheckedKeys // [Legacy] TODO: not in API
|
||||
traverseTreeNodes(children, ({ node, pos, key }) => {
|
||||
if (checkedKeySet[key]) {
|
||||
eventObj.checkedNodes.push(node)
|
||||
eventObj.checkedNodesPositions.push({ node, pos })
|
||||
}
|
||||
eventObj.checkedNodesPositions = []
|
||||
eventObj.halfCheckedKeys = halfCheckedKeys
|
||||
|
||||
checkedKeys.forEach((key) => {
|
||||
const entity = keyEntities[key]
|
||||
if (!entity) return
|
||||
|
||||
const { node, pos } = entity
|
||||
|
||||
eventObj.checkedNodes.push(node)
|
||||
eventObj.checkedNodesPositions.push({ node, pos })
|
||||
})
|
||||
|
||||
this.setUncontrolledState({
|
||||
checkedKeys: newCheckedKeys,
|
||||
halfCheckedKeys: newHalfCheckedKeys,
|
||||
_checkedKeys: checkedKeys,
|
||||
_halfCheckedKeys: halfCheckedKeys,
|
||||
})
|
||||
}
|
||||
this.__emit('check', selectedObj, eventObj)
|
||||
this.__emit('check', checkedObj, eventObj)
|
||||
},
|
||||
onNodeLoad (treeNode) {
|
||||
return new Promise((resolve) => {
|
||||
// We need to get the latest state of loading/loaded keys
|
||||
this.setState(({ _loadedKeys: loadedKeys = [], _loadingKeys: loadingKeys = [] }) => {
|
||||
const { loadData } = this.$props
|
||||
const { eventKey } = getOptionProps('treeNode')
|
||||
|
||||
// Clean up
|
||||
this.checkedBatch = null
|
||||
if (!loadData || loadedKeys.indexOf(eventKey) !== -1 || loadingKeys.indexOf(eventKey) !== -1) {
|
||||
return {}
|
||||
}
|
||||
|
||||
// Process load data
|
||||
const promise = loadData(treeNode)
|
||||
promise.then(() => {
|
||||
const newLoadedKeys = arrAdd(this.$data._loadedKeys, eventKey)
|
||||
this.setUncontrolledState({
|
||||
_loadedKeys: newLoadedKeys,
|
||||
})
|
||||
this.setState({
|
||||
_loadingKeys: arrDel(this.$data._loadingKeys, eventKey),
|
||||
})
|
||||
const eventObj = {
|
||||
event: 'load',
|
||||
node: treeNode,
|
||||
}
|
||||
this.__emit('load', eventObj)
|
||||
|
||||
resolve()
|
||||
})
|
||||
|
||||
return {
|
||||
loadingKeys: arrAdd(loadingKeys, eventKey),
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onNodeExpand (e, treeNode) {
|
||||
const { sExpandedKeys, loadData } = this
|
||||
let expandedKeys = [...sExpandedKeys]
|
||||
let { _expandedKeys: expandedKeys } = this.$data
|
||||
const { loadData } = this.$props
|
||||
const { eventKey, expanded } = getOptionProps(treeNode)
|
||||
|
||||
// Update selected keys
|
||||
|
@ -490,7 +496,7 @@ const Tree = {
|
|||
expandedKeys = arrDel(expandedKeys, eventKey)
|
||||
}
|
||||
|
||||
this.setUncontrolledState({ expandedKeys })
|
||||
this.setUncontrolledState({ _expandedKeys: expandedKeys })
|
||||
this.__emit('expand', expandedKeys, {
|
||||
node: treeNode,
|
||||
expanded: targetExpanded,
|
||||
|
@ -502,7 +508,7 @@ const Tree = {
|
|||
const loadPromise = this.onNodeLoad(treeNode)
|
||||
return loadPromise ? loadPromise.then(() => {
|
||||
// [Legacy] Refresh logic
|
||||
this.setUncontrolledState({ expandedKeys })
|
||||
this.setUncontrolledState({ _expandedKeys: expandedKeys })
|
||||
}) : null
|
||||
}
|
||||
|
||||
|
@ -519,27 +525,7 @@ const Tree = {
|
|||
|
||||
onNodeContextMenu (event, node) {
|
||||
event.preventDefault()
|
||||
this.__emit('rightClick', { event, node })
|
||||
},
|
||||
|
||||
/**
|
||||
* Sync state with props if needed
|
||||
*/
|
||||
getSyncProps (props = {}) {
|
||||
const newState = {}
|
||||
const children = this.$slots.default
|
||||
if (props.selectedKeys !== undefined) {
|
||||
newState.sSelectedKeys = calcSelectedKeys(props.selectedKeys, props, children)
|
||||
}
|
||||
|
||||
if (props.checkedKeys !== undefined) {
|
||||
const { checkedKeys = [], halfCheckedKeys = [] } =
|
||||
calcCheckedKeys(props.checkedKeys, props, children) || {}
|
||||
newState.sCheckedKeys = checkedKeys
|
||||
newState.sHalfCheckedKeys = halfCheckedKeys
|
||||
}
|
||||
|
||||
return newState
|
||||
this.__emit('rightclick', { event, node })
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -550,11 +536,9 @@ const Tree = {
|
|||
const newState = {}
|
||||
const props = getOptionProps(this)
|
||||
Object.keys(state).forEach(name => {
|
||||
if (name in props) return
|
||||
|
||||
if (name.replace('_', '') in props) return
|
||||
needSync = true
|
||||
const key = this.propsToStateMap[name]
|
||||
newState[key] = state[name]
|
||||
newState[name] = state[name]
|
||||
})
|
||||
|
||||
if (needSync) {
|
||||
|
@ -563,8 +547,8 @@ const Tree = {
|
|||
},
|
||||
|
||||
isKeyChecked (key) {
|
||||
const { sCheckedKeys = [] } = this
|
||||
return sCheckedKeys.indexOf(key) !== -1
|
||||
const { _checkedKeys: checkedKeys = [] } = this.$data
|
||||
return checkedKeys.indexOf(key) !== -1
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -573,23 +557,31 @@ const Tree = {
|
|||
*/
|
||||
renderTreeNode (child, index, level = 0) {
|
||||
const {
|
||||
sExpandedKeys = [], sSelectedKeys = [], sHalfCheckedKeys = [],
|
||||
sLoadedKeys = [], sLoadingKeys = [],
|
||||
dragOverNodeKey, dropPosition,
|
||||
} = this
|
||||
_keyEntities: keyEntities,
|
||||
_expandedKeys: expandedKeys = [],
|
||||
_selectedKeys: selectedKeys = [],
|
||||
_halfCheckedKeys: halfCheckedKeys = [],
|
||||
_loadedKeys: loadedKeys = [], _loadingKeys: loadingKeys = [],
|
||||
_dragOverNodeKey: dragOverNodeKey,
|
||||
_dropPosition: dropPosition,
|
||||
} = this.$data
|
||||
const pos = getPosition(level, index)
|
||||
const key = child.key || pos
|
||||
if (!keyEntities[key]) {
|
||||
warnOnlyTreeNode()
|
||||
return null
|
||||
}
|
||||
|
||||
return cloneElement(child, {
|
||||
props: {
|
||||
key,
|
||||
eventKey: key,
|
||||
expanded: sExpandedKeys.indexOf(key) !== -1,
|
||||
selected: sSelectedKeys.indexOf(key) !== -1,
|
||||
loaded: sLoadedKeys.indexOf(key) !== -1,
|
||||
loading: sLoadingKeys.indexOf(key) !== -1,
|
||||
expanded: expandedKeys.indexOf(key) !== -1,
|
||||
selected: selectedKeys.indexOf(key) !== -1,
|
||||
loaded: loadedKeys.indexOf(key) !== -1,
|
||||
loading: loadingKeys.indexOf(key) !== -1,
|
||||
checked: this.isKeyChecked(key),
|
||||
halfChecked: sHalfCheckedKeys.indexOf(key) !== -1,
|
||||
halfChecked: halfCheckedKeys.indexOf(key) !== -1,
|
||||
pos,
|
||||
|
||||
// [Legacy] Drag props
|
||||
|
@ -597,17 +589,16 @@ const Tree = {
|
|||
dragOverGapTop: dragOverNodeKey === key && dropPosition === -1,
|
||||
dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1,
|
||||
},
|
||||
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { _treeNode: treeNode } = this.$data
|
||||
const {
|
||||
prefixCls, focusable,
|
||||
showLine,
|
||||
$slots: { default: children = [] },
|
||||
} = this
|
||||
showLine, tabIndex = 0,
|
||||
} = this.$props
|
||||
const domProps = {}
|
||||
|
||||
return (
|
||||
|
@ -618,10 +609,10 @@ const Tree = {
|
|||
})}
|
||||
role='tree-node'
|
||||
unselectable='on'
|
||||
tabIndex={focusable ? '0' : null}
|
||||
tabIndex={focusable ? tabIndex : null}
|
||||
onKeydown={focusable ? this.onKeydown : () => {}}
|
||||
>
|
||||
{mapChildren(children, (node, index) => (
|
||||
{mapChildren(treeNode, (node, index) => (
|
||||
this.renderTreeNode(node, index)
|
||||
))}
|
||||
</ul>
|
||||
|
@ -629,4 +620,6 @@ const Tree = {
|
|||
},
|
||||
}
|
||||
|
||||
export default Tree
|
||||
export { Tree }
|
||||
|
||||
export default proxyComponent(Tree)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import PropTypes from '../../_util/vue-types'
|
||||
import classNames from 'classnames'
|
||||
import warning from 'warning'
|
||||
import { getPosition, getNodeChildren, isCheckDisabled, traverseTreeNodes, mapChildren } from './util'
|
||||
import { initDefaultProps, getOptionProps, filterEmpty, getComponentFromProp } from '../../_util/props-util'
|
||||
import { getNodeChildren,
|
||||
mapChildren,
|
||||
warnOnlyTreeNode } from './util'
|
||||
import { initDefaultProps, filterEmpty, getComponentFromProp } from '../../_util/props-util'
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import getTransitionProps from '../../_util/getTransitionProps'
|
||||
import { cloneElement } from '../../_util/vnode'
|
||||
|
||||
function noop () {}
|
||||
const ICON_OPEN = 'open'
|
||||
|
@ -12,8 +14,6 @@ const ICON_CLOSE = 'close'
|
|||
|
||||
const defaultTitle = '---'
|
||||
|
||||
let onlyTreeNodeWarned = false // Only accept TreeNode
|
||||
|
||||
const TreeNode = {
|
||||
name: 'TreeNode',
|
||||
mixins: [BaseMixin],
|
||||
|
@ -45,6 +45,7 @@ const TreeNode = {
|
|||
disableCheckbox: PropTypes.bool,
|
||||
icon: PropTypes.any,
|
||||
dataRef: PropTypes.object,
|
||||
switcherIcon: PropTypes.any,
|
||||
}, {}),
|
||||
|
||||
data () {
|
||||
|
@ -66,84 +67,11 @@ const TreeNode = {
|
|||
mounted () {
|
||||
this.syncLoadData(this.$props)
|
||||
},
|
||||
watch: {
|
||||
expanded (val) {
|
||||
this.syncLoadData({ expanded: val })
|
||||
},
|
||||
updated () {
|
||||
this.syncLoadData(this.$props)
|
||||
},
|
||||
|
||||
methods: {
|
||||
onUpCheckConduct (treeNode, nodeChecked, nodeHalfChecked, e) {
|
||||
const { pos: nodePos } = getOptionProps(treeNode)
|
||||
const { eventKey, pos, checked, halfChecked } = this
|
||||
const {
|
||||
vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck, onCheckConductFinished },
|
||||
vcTreeNode: { onUpCheckConduct } = {},
|
||||
} = this
|
||||
|
||||
// Stop conduct when current node is disabled
|
||||
if (isCheckDisabled(this)) {
|
||||
onCheckConductFinished(e)
|
||||
return
|
||||
}
|
||||
|
||||
const children = this.getNodeChildren()
|
||||
|
||||
let checkedCount = nodeChecked ? 1 : 0
|
||||
|
||||
// Statistic checked count
|
||||
children.forEach((node, index) => {
|
||||
const childPos = getPosition(pos, index)
|
||||
|
||||
if (nodePos === childPos || isCheckDisabled(node)) {
|
||||
return
|
||||
}
|
||||
if (isKeyChecked(node.key || childPos)) {
|
||||
checkedCount += 1
|
||||
}
|
||||
})
|
||||
|
||||
// Static enabled children count
|
||||
const enabledChildrenCount = children
|
||||
.filter(node => !isCheckDisabled(node))
|
||||
.length
|
||||
|
||||
// checkStrictly will not conduct check status
|
||||
const nextChecked = checkStrictly ? checked : enabledChildrenCount === checkedCount
|
||||
const nextHalfChecked = checkStrictly // propagated or child checked
|
||||
? halfChecked : (nodeHalfChecked || (checkedCount > 0 && !nextChecked))
|
||||
|
||||
// Add into batch update
|
||||
if (checked !== nextChecked || halfChecked !== nextHalfChecked) {
|
||||
onBatchNodeCheck(eventKey, nextChecked, nextHalfChecked)
|
||||
|
||||
if (onUpCheckConduct) {
|
||||
onUpCheckConduct(this, nextChecked, nextHalfChecked, e)
|
||||
} else {
|
||||
// Flush all the update
|
||||
onCheckConductFinished(e)
|
||||
}
|
||||
} else {
|
||||
// Flush all the update
|
||||
onCheckConductFinished(e)
|
||||
}
|
||||
},
|
||||
|
||||
onDownCheckConduct (nodeChecked) {
|
||||
const { $slots } = this
|
||||
const children = $slots.default || []
|
||||
const { vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this
|
||||
if (checkStrictly) return
|
||||
|
||||
traverseTreeNodes(children, ({ node, key }) => {
|
||||
if (isCheckDisabled(node)) return false
|
||||
|
||||
if (nodeChecked !== isKeyChecked(key)) {
|
||||
onBatchNodeCheck(key, nodeChecked, false)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onSelectorClick (e) {
|
||||
// Click trigger before select/check operation
|
||||
const { vcTree: { onNodeClick }} = this
|
||||
|
@ -171,27 +99,16 @@ const TreeNode = {
|
|||
onCheck (e) {
|
||||
if (this.isDisabled()) return
|
||||
|
||||
const { disableCheckbox, checked, eventKey } = this
|
||||
const { disableCheckbox, checked } = this
|
||||
const {
|
||||
vcTree: { checkable, onBatchNodeCheck, onCheckConductFinished },
|
||||
vcTreeNode: { onUpCheckConduct } = {},
|
||||
vcTree: { checkable, onNodeCheck },
|
||||
} = this
|
||||
|
||||
if (!checkable || disableCheckbox) return
|
||||
|
||||
e.preventDefault()
|
||||
const targetChecked = !checked
|
||||
onBatchNodeCheck(eventKey, targetChecked, false, this)
|
||||
|
||||
// Children conduct
|
||||
this.onDownCheckConduct(targetChecked)
|
||||
|
||||
// Parent conduct
|
||||
if (onUpCheckConduct) {
|
||||
onUpCheckConduct(this, targetChecked, false, e)
|
||||
} else {
|
||||
onCheckConductFinished(e)
|
||||
}
|
||||
onNodeCheck(e, this, targetChecked)
|
||||
},
|
||||
|
||||
onMouseEnter (e) {
|
||||
|
@ -282,9 +199,8 @@ const TreeNode = {
|
|||
const originList = filterEmpty(children)
|
||||
const targetList = getNodeChildren(originList)
|
||||
|
||||
if (originList.length !== targetList.length && !onlyTreeNodeWarned) {
|
||||
onlyTreeNodeWarned = true
|
||||
warning(false, 'Tree only accept TreeNode as children.')
|
||||
if (originList.length !== targetList.length) {
|
||||
warnOnlyTreeNode()
|
||||
}
|
||||
|
||||
return targetList
|
||||
|
@ -341,15 +257,15 @@ const TreeNode = {
|
|||
|
||||
// Load data to avoid default expanded tree without data
|
||||
syncLoadData (props) {
|
||||
const { expanded } = this
|
||||
const { expanded, loading, loaded } = props
|
||||
const { vcTree: { onNodeLoad }} = this
|
||||
|
||||
if (loading) return
|
||||
// read from state to avoid loadData at same time
|
||||
if (expanded && !this.isLeaf2()) {
|
||||
// We needn't reload data when has children in sync logic
|
||||
// It's only needed in node expanded
|
||||
const hasChildren = this.getNodeChildren().length !== 0
|
||||
if (!hasChildren) {
|
||||
if (!hasChildren && !loaded) {
|
||||
onNodeLoad(this)
|
||||
}
|
||||
}
|
||||
|
@ -359,19 +275,22 @@ const TreeNode = {
|
|||
renderSwitcher () {
|
||||
const { expanded } = this
|
||||
const { vcTree: { prefixCls }} = this
|
||||
|
||||
const switcherIcon = getComponentFromProp(this, 'switcherIcon') || getComponentFromProp(this.vcTree, 'switcherIcon')
|
||||
if (this.isLeaf2()) {
|
||||
return <span class={`${prefixCls}-switcher ${prefixCls}-switcher-noop`} />
|
||||
return (
|
||||
<span key='switcher' class={classNames(`${prefixCls}-switcher`, `${prefixCls}-switcher-noop`)}>
|
||||
{typeof switcherIcon === 'function'
|
||||
? cloneElement(switcherIcon, { props: { ...this.$props, isLeaf: true }}) : switcherIcon}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const switcherCls = classNames(`${prefixCls}-switcher`, `${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`)
|
||||
return (
|
||||
<span
|
||||
class={classNames(
|
||||
`${prefixCls}-switcher`,
|
||||
`${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`,
|
||||
)}
|
||||
onClick={this.onExpand}
|
||||
/>
|
||||
<span key='switcher' onClick={this.onExpand} class={switcherCls}>
|
||||
{typeof switcherIcon === 'function'
|
||||
? cloneElement(switcherIcon, { props: { ...this.$props, isLeaf: false }}) : switcherIcon}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
|
||||
|
@ -388,6 +307,7 @@ const TreeNode = {
|
|||
|
||||
return (
|
||||
<span
|
||||
key='checkbox'
|
||||
class={classNames(
|
||||
`${prefixCls}-checkbox`,
|
||||
checked && `${prefixCls}-checkbox-checked`,
|
||||
|
@ -407,6 +327,7 @@ const TreeNode = {
|
|||
|
||||
return (
|
||||
<span
|
||||
key='icon'
|
||||
class={classNames(
|
||||
`${prefixCls}-iconEle`,
|
||||
`${prefixCls}-icon__${this.getNodeState() || 'docu'}`,
|
||||
|
@ -450,6 +371,7 @@ const TreeNode = {
|
|||
|
||||
return (
|
||||
<span
|
||||
key='selector'
|
||||
ref='selectHandle'
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
class={classNames(
|
||||
|
@ -481,23 +403,12 @@ const TreeNode = {
|
|||
renderTreeNode,
|
||||
}} = this
|
||||
|
||||
// [Legacy] Animation control
|
||||
const renderFirst = this.renderFirst
|
||||
this.renderFirst = 1
|
||||
let transitionAppear = true
|
||||
if (!renderFirst && expanded) {
|
||||
transitionAppear = false
|
||||
}
|
||||
|
||||
let animProps = {}
|
||||
if (openTransitionName) {
|
||||
animProps = getTransitionProps(openTransitionName, { appear: transitionAppear })
|
||||
animProps = getTransitionProps(openTransitionName)
|
||||
} else if (typeof openAnimation === 'object') {
|
||||
animProps = { ...openAnimation }
|
||||
animProps.props = { css: false, ...animProps.props }
|
||||
if (!transitionAppear) {
|
||||
delete animProps.props.appear
|
||||
}
|
||||
}
|
||||
|
||||
// Children TreeNode
|
||||
|
@ -516,6 +427,7 @@ const TreeNode = {
|
|||
expanded && `${prefixCls}-child-tree-open`,
|
||||
)}
|
||||
data-expanded={expanded}
|
||||
role='group'
|
||||
>
|
||||
{mapChildren(nodeList, (node, index) => (
|
||||
renderTreeNode(node, index, pos)
|
||||
|
@ -560,6 +472,7 @@ const TreeNode = {
|
|||
'drag-over-gap-bottom': !disabled && dragOverGapBottom,
|
||||
'filter-node': filterTreeNode && filterTreeNode(this),
|
||||
}}
|
||||
role='treeitem'
|
||||
onDragenter={draggable ? this.onDragEnter : noop}
|
||||
onDragover={draggable ? this.onDragOver : noop}
|
||||
onDragleave={draggable ? this.onDragLeave : noop}
|
||||
|
|
|
@ -1,25 +1,7 @@
|
|||
import { getOptionProps } from '../../_util/props-util'
|
||||
import Tree from './Tree'
|
||||
import ProxyTree, { Tree } from './Tree'
|
||||
import TreeNode from './TreeNode'
|
||||
Tree.TreeNode = TreeNode
|
||||
ProxyTree.TreeNode = TreeNode
|
||||
|
||||
//
|
||||
const NewTree = {
|
||||
TreeNode: TreeNode,
|
||||
props: Tree.props,
|
||||
render () {
|
||||
const { $listeners, $slots } = this
|
||||
const treeProps = {
|
||||
props: {
|
||||
...getOptionProps(this),
|
||||
children: $slots.default,
|
||||
},
|
||||
on: $listeners,
|
||||
}
|
||||
return (
|
||||
<Tree {...treeProps}>{$slots.default}</Tree>
|
||||
)
|
||||
},
|
||||
}
|
||||
export { TreeNode }
|
||||
export default NewTree
|
||||
export { Tree, TreeNode }
|
||||
export default ProxyTree
|
||||
|
|
|
@ -0,0 +1,378 @@
|
|||
/* eslint no-loop-func: 0*/
|
||||
import warning from 'warning'
|
||||
import { getSlotOptions, getOptionProps } from '../../_util/props-util'
|
||||
const DRAG_SIDE_RANGE = 0.25
|
||||
const DRAG_MIN_GAP = 2
|
||||
|
||||
let onlyTreeNodeWarned = false
|
||||
|
||||
export function warnOnlyTreeNode () {
|
||||
if (onlyTreeNodeWarned) return
|
||||
|
||||
onlyTreeNodeWarned = true
|
||||
warning(false, 'Tree only accept TreeNode as children.')
|
||||
}
|
||||
|
||||
export function arrDel (list, value) {
|
||||
const clone = list.slice()
|
||||
const index = clone.indexOf(value)
|
||||
if (index >= 0) {
|
||||
clone.splice(index, 1)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
export function arrAdd (list, value) {
|
||||
const clone = list.slice()
|
||||
if (clone.indexOf(value) === -1) {
|
||||
clone.push(value)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
export function posToArr (pos) {
|
||||
return pos.split('-')
|
||||
}
|
||||
|
||||
export function getPosition (level, index) {
|
||||
return `${level}-${index}`
|
||||
}
|
||||
|
||||
export function isTreeNode (node) {
|
||||
return getSlotOptions(node).isTreeNode
|
||||
}
|
||||
|
||||
export function getNodeChildren (children = []) {
|
||||
return children.filter(isTreeNode)
|
||||
}
|
||||
|
||||
export function isCheckDisabled (node) {
|
||||
const { disabled, disableCheckbox } = getOptionProps(node) || {}
|
||||
return !!(disabled || disableCheckbox)
|
||||
}
|
||||
|
||||
export function traverseTreeNodes (treeNodes, subTreeData, callback) {
|
||||
function processNode (node, index, parent) {
|
||||
const children = node ? node.componentOptions.children : treeNodes
|
||||
const pos = node ? getPosition(parent.pos, index) : 0
|
||||
|
||||
// Filter children
|
||||
const childList = getNodeChildren(children)
|
||||
|
||||
// Process node if is not root
|
||||
if (node) {
|
||||
const data = {
|
||||
node,
|
||||
index,
|
||||
pos,
|
||||
key: node.key || pos,
|
||||
parentPos: parent.node ? parent.pos : null,
|
||||
}
|
||||
callback(data)
|
||||
}
|
||||
|
||||
// Process children node
|
||||
childList.forEach((subNode, subIndex) => {
|
||||
processNode(subNode, subIndex, { node, pos })
|
||||
})
|
||||
}
|
||||
|
||||
processNode(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `rc-util` `toArray` to get the children list which keeps the key.
|
||||
* And return single node if children is only one(This can avoid `key` missing check).
|
||||
*/
|
||||
export function mapChildren (children = [], func) {
|
||||
const list = children.map(func)
|
||||
if (list.length === 1) {
|
||||
return list[0]
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* [Legacy] Return halfChecked when it has value.
|
||||
* @param checkedKeys
|
||||
* @param halfChecked
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getStrictlyValue (checkedKeys, halfChecked) {
|
||||
if (halfChecked) {
|
||||
return { checked: checkedKeys, halfChecked }
|
||||
}
|
||||
return checkedKeys
|
||||
}
|
||||
|
||||
export function getFullKeyList (treeNodes) {
|
||||
const keyList = []
|
||||
traverseTreeNodes(treeNodes, ({ key }) => {
|
||||
keyList.push(key)
|
||||
})
|
||||
return keyList
|
||||
}
|
||||
|
||||
/**
|
||||
* Check position relation.
|
||||
* @param parentPos
|
||||
* @param childPos
|
||||
* @param directly only directly parent can be true
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isParent (parentPos, childPos, directly = false) {
|
||||
if (!parentPos || !childPos || parentPos.length > childPos.length) return false
|
||||
|
||||
const parentPath = posToArr(parentPos)
|
||||
const childPath = posToArr(childPos)
|
||||
|
||||
// Directly check
|
||||
if (directly && parentPath.length !== childPath.length - 1) return false
|
||||
|
||||
const len = parentPath.length
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
if (parentPath[i] !== childPath[i]) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistic TreeNodes info
|
||||
* @param treeNodes
|
||||
* @returns {{}}
|
||||
*/
|
||||
export function getNodesStatistic (treeNodes) {
|
||||
const statistic = {
|
||||
keyNodes: {},
|
||||
posNodes: {},
|
||||
nodeList: [],
|
||||
}
|
||||
|
||||
traverseTreeNodes(treeNodes, true, ({ node, index, pos, key, subNodes, parentPos }) => {
|
||||
const data = { node, index, pos, key, subNodes, parentPos }
|
||||
statistic.keyNodes[key] = data
|
||||
statistic.posNodes[pos] = data
|
||||
statistic.nodeList.push(data)
|
||||
})
|
||||
|
||||
return statistic
|
||||
}
|
||||
|
||||
export function getDragNodesKeys (treeNodes, node) {
|
||||
const { eventKey, pos } = getOptionProps(node)
|
||||
const dragNodesKeys = []
|
||||
|
||||
traverseTreeNodes(treeNodes, ({ pos: nodePos, key }) => {
|
||||
if (isParent(pos, nodePos)) {
|
||||
dragNodesKeys.push(key)
|
||||
}
|
||||
})
|
||||
dragNodesKeys.push(eventKey || pos)
|
||||
return dragNodesKeys
|
||||
}
|
||||
|
||||
export function calcDropPosition (event, treeNode) {
|
||||
const { clientY } = event
|
||||
const { top, bottom, height } = treeNode.$refs.selectHandle.getBoundingClientRect()
|
||||
const des = Math.max(height * DRAG_SIDE_RANGE, DRAG_MIN_GAP)
|
||||
|
||||
if (clientY <= top + des) {
|
||||
return -1
|
||||
} else if (clientY >= bottom - des) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto expand all related node when sub node is expanded
|
||||
* @param keyList
|
||||
* @param props
|
||||
* @returns [string]
|
||||
*/
|
||||
export function calcExpandedKeys (keyList, props, children = []) {
|
||||
if (!keyList) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Fill parent expanded keys
|
||||
const { keyNodes, nodeList } = getNodesStatistic(children)
|
||||
const needExpandKeys = {}
|
||||
const needExpandPathList = []
|
||||
|
||||
// Fill expanded nodes
|
||||
keyList.forEach((key) => {
|
||||
const node = keyNodes[key]
|
||||
if (node) {
|
||||
needExpandKeys[key] = true
|
||||
needExpandPathList.push(node.pos)
|
||||
}
|
||||
})
|
||||
|
||||
// Match parent by path
|
||||
nodeList.forEach(({ pos, key }) => {
|
||||
if (needExpandPathList.some(childPos => isParent(pos, childPos))) {
|
||||
needExpandKeys[key] = true
|
||||
}
|
||||
})
|
||||
|
||||
const calcExpandedKeyList = Object.keys(needExpandKeys)
|
||||
|
||||
// [Legacy] Return origin keyList if calc list is empty
|
||||
return calcExpandedKeyList.length ? calcExpandedKeyList : keyList
|
||||
}
|
||||
|
||||
/**
|
||||
* Return selectedKeys according with multiple prop
|
||||
* @param selectedKeys
|
||||
* @param props
|
||||
* @returns [string]
|
||||
*/
|
||||
export function calcSelectedKeys (selectedKeys, props) {
|
||||
if (!selectedKeys) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const { multiple } = props
|
||||
if (multiple) {
|
||||
return selectedKeys.slice()
|
||||
}
|
||||
|
||||
if (selectedKeys.length) {
|
||||
return [selectedKeys[0]]
|
||||
}
|
||||
return selectedKeys
|
||||
}
|
||||
|
||||
/**
|
||||
* Check conduct is by key level. It pass though up & down.
|
||||
* When conduct target node is check means already conducted will be skip.
|
||||
* @param treeNodes
|
||||
* @param checkedKeys
|
||||
* @returns {{checkedKeys: Array, halfCheckedKeys: Array}}
|
||||
*/
|
||||
export function calcCheckStateConduct (treeNodes, checkedKeys) {
|
||||
const { keyNodes, posNodes } = getNodesStatistic(treeNodes)
|
||||
|
||||
const tgtCheckedKeys = {}
|
||||
const tgtHalfCheckedKeys = {}
|
||||
|
||||
// Conduct up
|
||||
function conductUp (key, halfChecked) {
|
||||
if (tgtCheckedKeys[key]) return
|
||||
|
||||
const { subNodes = [], parentPos, node } = keyNodes[key]
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
const allSubChecked = !halfChecked && subNodes
|
||||
.filter(sub => !isCheckDisabled(sub.node))
|
||||
.every(sub => tgtCheckedKeys[sub.key])
|
||||
|
||||
if (allSubChecked) {
|
||||
tgtCheckedKeys[key] = true
|
||||
} else {
|
||||
tgtHalfCheckedKeys[key] = true
|
||||
}
|
||||
|
||||
if (parentPos !== null) {
|
||||
conductUp(posNodes[parentPos].key, !allSubChecked)
|
||||
}
|
||||
}
|
||||
|
||||
// Conduct down
|
||||
function conductDown (key) {
|
||||
if (tgtCheckedKeys[key]) return
|
||||
const { subNodes = [], node } = keyNodes[key]
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
tgtCheckedKeys[key] = true
|
||||
|
||||
subNodes.forEach((sub) => {
|
||||
conductDown(sub.key)
|
||||
})
|
||||
}
|
||||
|
||||
function conduct (key) {
|
||||
if (!keyNodes[key]) {
|
||||
warning(false, `'${key}' does not exist in the tree.`)
|
||||
return
|
||||
}
|
||||
|
||||
const { subNodes = [], parentPos, node } = keyNodes[key]
|
||||
|
||||
tgtCheckedKeys[key] = true
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
// Conduct down
|
||||
subNodes
|
||||
.filter(sub => !isCheckDisabled(sub.node))
|
||||
.forEach((sub) => {
|
||||
conductDown(sub.key)
|
||||
})
|
||||
|
||||
// Conduct up
|
||||
if (parentPos !== null) {
|
||||
conductUp(posNodes[parentPos].key)
|
||||
}
|
||||
}
|
||||
|
||||
checkedKeys.forEach((key) => {
|
||||
conduct(key)
|
||||
})
|
||||
|
||||
return {
|
||||
checkedKeys: Object.keys(tgtCheckedKeys),
|
||||
halfCheckedKeys: Object.keys(tgtHalfCheckedKeys)
|
||||
.filter(key => !tgtCheckedKeys[key]),
|
||||
}
|
||||
}
|
||||
|
||||
function keyListToString (keyList) {
|
||||
if (!keyList) return keyList
|
||||
return keyList.map(key => String(key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the value of checked and halfChecked keys.
|
||||
* This should be only run in init or props changed.
|
||||
*/
|
||||
export function calcCheckedKeys (keys, props, children = []) {
|
||||
const { checkable, checkStrictly } = props
|
||||
|
||||
if (!checkable || !keys) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Convert keys to object format
|
||||
let keyProps
|
||||
if (Array.isArray(keys)) {
|
||||
// [Legacy] Follow the api doc
|
||||
keyProps = {
|
||||
checkedKeys: keys,
|
||||
halfCheckedKeys: undefined,
|
||||
}
|
||||
} else if (typeof keys === 'object') {
|
||||
keyProps = {
|
||||
checkedKeys: keys.checked || undefined,
|
||||
halfCheckedKeys: keys.halfChecked || undefined,
|
||||
}
|
||||
} else {
|
||||
warning(false, '`CheckedKeys` is not an array or an object')
|
||||
return null
|
||||
}
|
||||
|
||||
keyProps.checkedKeys = keyListToString(keyProps.checkedKeys)
|
||||
keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys)
|
||||
|
||||
// Do nothing if is checkStrictly mode
|
||||
if (checkStrictly) {
|
||||
return keyProps
|
||||
}
|
||||
|
||||
// Conduct calculate the check status
|
||||
const { checkedKeys = [] } = keyProps
|
||||
return calcCheckStateConduct(children, checkedKeys)
|
||||
}
|
|
@ -1,9 +1,20 @@
|
|||
/* eslint no-loop-func: 0*/
|
||||
import warning from 'warning'
|
||||
import omit from 'omit.js'
|
||||
import TreeNode from './TreeNode'
|
||||
import { getSlotOptions, getOptionProps } from '../../_util/props-util'
|
||||
const DRAG_SIDE_RANGE = 0.25
|
||||
const DRAG_MIN_GAP = 2
|
||||
|
||||
let onlyTreeNodeWarned = false
|
||||
|
||||
export function warnOnlyTreeNode () {
|
||||
if (onlyTreeNodeWarned) return
|
||||
|
||||
onlyTreeNodeWarned = true
|
||||
warning(false, 'Tree only accept TreeNode as children.')
|
||||
}
|
||||
|
||||
export function arrDel (list, value) {
|
||||
const clone = list.slice()
|
||||
const index = clone.indexOf(value)
|
||||
|
@ -29,9 +40,12 @@ export function getPosition (level, index) {
|
|||
return `${level}-${index}`
|
||||
}
|
||||
|
||||
export function isTreeNode (node) {
|
||||
return getSlotOptions(node).isTreeNode
|
||||
}
|
||||
|
||||
export function getNodeChildren (children = []) {
|
||||
return children
|
||||
.filter(child => getSlotOptions(child).isTreeNode)
|
||||
return children.filter(isTreeNode)
|
||||
}
|
||||
|
||||
export function isCheckDisabled (node) {
|
||||
|
@ -39,12 +53,7 @@ export function isCheckDisabled (node) {
|
|||
return !!(disabled || disableCheckbox)
|
||||
}
|
||||
|
||||
export function traverseTreeNodes (treeNodes, subTreeData, callback) {
|
||||
if (typeof subTreeData === 'function') {
|
||||
callback = subTreeData
|
||||
subTreeData = false
|
||||
}
|
||||
|
||||
export function traverseTreeNodes (treeNodes, callback) {
|
||||
function processNode (node, index, parent) {
|
||||
const children = node ? node.componentOptions.children : treeNodes
|
||||
const pos = node ? getPosition(parent.pos, index) : 0
|
||||
|
@ -61,28 +70,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) {
|
|||
key: node.key || pos,
|
||||
parentPos: parent.node ? parent.pos : null,
|
||||
}
|
||||
|
||||
// Children data is not must have
|
||||
if (subTreeData) {
|
||||
// Statistic children
|
||||
const subNodes = []
|
||||
childList.forEach((subNode, subIndex) => {
|
||||
// Provide limit snapshot
|
||||
const subPos = getPosition(pos, index)
|
||||
subNodes.push({
|
||||
node: subNode,
|
||||
key: subNode.key || subPos,
|
||||
pos: subPos,
|
||||
index: subIndex,
|
||||
})
|
||||
})
|
||||
data.subNodes = subNodes
|
||||
}
|
||||
|
||||
// Can break traverse by return false
|
||||
if (callback(data) === false) {
|
||||
return
|
||||
}
|
||||
callback(data)
|
||||
}
|
||||
|
||||
// Process children node
|
||||
|
@ -106,81 +94,12 @@ export function mapChildren (children = [], func) {
|
|||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* [Legacy] Return halfChecked when it has value.
|
||||
* @param checkedKeys
|
||||
* @param halfChecked
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getStrictlyValue (checkedKeys, halfChecked) {
|
||||
if (halfChecked) {
|
||||
return { checked: checkedKeys, halfChecked }
|
||||
}
|
||||
return checkedKeys
|
||||
}
|
||||
|
||||
export function getFullKeyList (treeNodes) {
|
||||
const keyList = []
|
||||
traverseTreeNodes(treeNodes, ({ key }) => {
|
||||
keyList.push(key)
|
||||
})
|
||||
return keyList
|
||||
}
|
||||
|
||||
/**
|
||||
* Check position relation.
|
||||
* @param parentPos
|
||||
* @param childPos
|
||||
* @param directly only directly parent can be true
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isParent (parentPos, childPos, directly = false) {
|
||||
if (!parentPos || !childPos || parentPos.length > childPos.length) return false
|
||||
|
||||
const parentPath = posToArr(parentPos)
|
||||
const childPath = posToArr(childPos)
|
||||
|
||||
// Directly check
|
||||
if (directly && parentPath.length !== childPath.length - 1) return false
|
||||
|
||||
const len = parentPath.length
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
if (parentPath[i] !== childPath[i]) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistic TreeNodes info
|
||||
* @param treeNodes
|
||||
* @returns {{}}
|
||||
*/
|
||||
export function getNodesStatistic (treeNodes) {
|
||||
const statistic = {
|
||||
keyNodes: {},
|
||||
posNodes: {},
|
||||
nodeList: [],
|
||||
}
|
||||
|
||||
traverseTreeNodes(treeNodes, true, ({ node, index, pos, key, subNodes, parentPos }) => {
|
||||
const data = { node, index, pos, key, subNodes, parentPos }
|
||||
statistic.keyNodes[key] = data
|
||||
statistic.posNodes[pos] = data
|
||||
statistic.nodeList.push(data)
|
||||
})
|
||||
|
||||
return statistic
|
||||
}
|
||||
|
||||
export function getDragNodesKeys (treeNodes, node) {
|
||||
const { eventKey, pos } = getOptionProps(node)
|
||||
const dragNodesKeys = []
|
||||
|
||||
traverseTreeNodes(treeNodes, ({ pos: nodePos, key }) => {
|
||||
if (isParent(pos, nodePos)) {
|
||||
dragNodesKeys.push(key)
|
||||
}
|
||||
traverseTreeNodes(treeNodes, ({ key }) => {
|
||||
dragNodesKeys.push(key)
|
||||
})
|
||||
dragNodesKeys.push(eventKey || pos)
|
||||
return dragNodesKeys
|
||||
|
@ -199,44 +118,6 @@ export function calcDropPosition (event, treeNode) {
|
|||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto expand all related node when sub node is expanded
|
||||
* @param keyList
|
||||
* @param props
|
||||
* @returns [string]
|
||||
*/
|
||||
export function calcExpandedKeys (keyList, props, children = []) {
|
||||
if (!keyList) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Fill parent expanded keys
|
||||
const { keyNodes, nodeList } = getNodesStatistic(children)
|
||||
const needExpandKeys = {}
|
||||
const needExpandPathList = []
|
||||
|
||||
// Fill expanded nodes
|
||||
keyList.forEach((key) => {
|
||||
const node = keyNodes[key]
|
||||
if (node) {
|
||||
needExpandKeys[key] = true
|
||||
needExpandPathList.push(node.pos)
|
||||
}
|
||||
})
|
||||
|
||||
// Match parent by path
|
||||
nodeList.forEach(({ pos, key }) => {
|
||||
if (needExpandPathList.some(childPos => isParent(pos, childPos))) {
|
||||
needExpandKeys[key] = true
|
||||
}
|
||||
})
|
||||
|
||||
const calcExpandedKeyList = Object.keys(needExpandKeys)
|
||||
|
||||
// [Legacy] Return origin keyList if calc list is empty
|
||||
return calcExpandedKeyList.length ? calcExpandedKeyList : keyList
|
||||
}
|
||||
|
||||
/**
|
||||
* Return selectedKeys according with multiple prop
|
||||
* @param selectedKeys
|
||||
|
@ -244,9 +125,7 @@ export function calcExpandedKeys (keyList, props, children = []) {
|
|||
* @returns [string]
|
||||
*/
|
||||
export function calcSelectedKeys (selectedKeys, props) {
|
||||
if (!selectedKeys) {
|
||||
return undefined
|
||||
}
|
||||
if (!selectedKeys) { return undefined }
|
||||
|
||||
const { multiple } = props
|
||||
if (multiple) {
|
||||
|
@ -260,103 +139,87 @@ export function calcSelectedKeys (selectedKeys, props) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check conduct is by key level. It pass though up & down.
|
||||
* When conduct target node is check means already conducted will be skip.
|
||||
* @param treeNodes
|
||||
* @param checkedKeys
|
||||
* @returns {{checkedKeys: Array, halfCheckedKeys: Array}}
|
||||
* Since React internal will convert key to string,
|
||||
* we need do this to avoid `checkStrictly` use number match
|
||||
*/
|
||||
export function calcCheckStateConduct (treeNodes, checkedKeys) {
|
||||
const { keyNodes, posNodes } = getNodesStatistic(treeNodes)
|
||||
|
||||
const tgtCheckedKeys = {}
|
||||
const tgtHalfCheckedKeys = {}
|
||||
|
||||
// Conduct up
|
||||
function conductUp (key, halfChecked) {
|
||||
if (tgtCheckedKeys[key]) return
|
||||
|
||||
const { subNodes = [], parentPos, node } = keyNodes[key]
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
const allSubChecked = !halfChecked && subNodes
|
||||
.filter(sub => !isCheckDisabled(sub.node))
|
||||
.every(sub => tgtCheckedKeys[sub.key])
|
||||
|
||||
if (allSubChecked) {
|
||||
tgtCheckedKeys[key] = true
|
||||
} else {
|
||||
tgtHalfCheckedKeys[key] = true
|
||||
}
|
||||
|
||||
if (parentPos !== null) {
|
||||
conductUp(posNodes[parentPos].key, !allSubChecked)
|
||||
}
|
||||
}
|
||||
|
||||
// Conduct down
|
||||
function conductDown (key) {
|
||||
if (tgtCheckedKeys[key]) return
|
||||
const { subNodes = [], node } = keyNodes[key]
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
tgtCheckedKeys[key] = true
|
||||
|
||||
subNodes.forEach((sub) => {
|
||||
conductDown(sub.key)
|
||||
})
|
||||
}
|
||||
|
||||
function conduct (key) {
|
||||
if (!keyNodes[key]) {
|
||||
warning(false, `'${key}' does not exist in the tree.`)
|
||||
return
|
||||
}
|
||||
|
||||
const { subNodes = [], parentPos, node } = keyNodes[key]
|
||||
|
||||
tgtCheckedKeys[key] = true
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
// Conduct down
|
||||
subNodes
|
||||
.filter(sub => !isCheckDisabled(sub.node))
|
||||
.forEach((sub) => {
|
||||
conductDown(sub.key)
|
||||
})
|
||||
|
||||
// Conduct up
|
||||
if (parentPos !== null) {
|
||||
conductUp(posNodes[parentPos].key)
|
||||
}
|
||||
}
|
||||
|
||||
checkedKeys.forEach((key) => {
|
||||
conduct(key)
|
||||
})
|
||||
|
||||
return {
|
||||
checkedKeys: Object.keys(tgtCheckedKeys),
|
||||
halfCheckedKeys: Object.keys(tgtHalfCheckedKeys)
|
||||
.filter(key => !tgtCheckedKeys[key]),
|
||||
}
|
||||
}
|
||||
|
||||
function keyListToString (keyList) {
|
||||
if (!keyList) return keyList
|
||||
return keyList.map(key => String(key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the value of checked and halfChecked keys.
|
||||
* This should be only run in init or props changed.
|
||||
*/
|
||||
export function calcCheckedKeys (keys, props, children = []) {
|
||||
const { checkable, checkStrictly } = props
|
||||
const internalProcessProps = (props = {}) => {
|
||||
return {
|
||||
props: omit(props, ['on', 'key', 'class', 'className', 'style']),
|
||||
on: props.on || {},
|
||||
class: props.class || props.className,
|
||||
style: props.style,
|
||||
key: props.key,
|
||||
}
|
||||
}
|
||||
export function convertDataToTree (h, treeData, processer) {
|
||||
if (!treeData) return []
|
||||
|
||||
if (!checkable || !keys) {
|
||||
const { processProps = internalProcessProps } = processer || {}
|
||||
const list = Array.isArray(treeData) ? treeData : [treeData]
|
||||
return list.map(({ children, ...props }) => {
|
||||
const childrenNodes = convertDataToTree(h, children, processer)
|
||||
return (
|
||||
<TreeNode {...processProps(props) }>
|
||||
{childrenNodes}
|
||||
</TreeNode>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: ========================= NEW LOGIC =========================
|
||||
/**
|
||||
* Calculate treeNodes entities. `processTreeEntity` is used for `rc-tree-select`
|
||||
* @param treeNodes
|
||||
* @param processTreeEntity User can customize the entity
|
||||
*/
|
||||
export function convertTreeToEntities (treeNodes, { initWrapper, processEntity, onProcessFinished } = {}) {
|
||||
const posEntities = {}
|
||||
const keyEntities = {}
|
||||
let wrapper = {
|
||||
posEntities,
|
||||
keyEntities,
|
||||
}
|
||||
|
||||
if (initWrapper) {
|
||||
wrapper = initWrapper(wrapper) || wrapper
|
||||
}
|
||||
|
||||
traverseTreeNodes(treeNodes, (item) => {
|
||||
const { node, index, pos, key, parentPos } = item
|
||||
const entity = { node, index, key, pos }
|
||||
|
||||
posEntities[pos] = entity
|
||||
keyEntities[key] = entity
|
||||
|
||||
// Fill children
|
||||
entity.parent = posEntities[parentPos]
|
||||
if (entity.parent) {
|
||||
entity.parent.children = entity.parent.children || []
|
||||
entity.parent.children.push(entity)
|
||||
}
|
||||
|
||||
if (processEntity) {
|
||||
processEntity(entity, wrapper)
|
||||
}
|
||||
})
|
||||
|
||||
if (onProcessFinished) {
|
||||
onProcessFinished(wrapper)
|
||||
}
|
||||
|
||||
return wrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse `checkedKeys` to { checkedKeys, halfCheckedKeys } style
|
||||
*/
|
||||
export function parseCheckedKeys (keys) {
|
||||
if (!keys) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -374,19 +237,186 @@ export function calcCheckedKeys (keys, props, children = []) {
|
|||
halfCheckedKeys: keys.halfChecked || undefined,
|
||||
}
|
||||
} else {
|
||||
warning(false, '`CheckedKeys` is not an array or an object')
|
||||
warning(false, '`checkedKeys` is not an array or an object')
|
||||
return null
|
||||
}
|
||||
|
||||
keyProps.checkedKeys = keyListToString(keyProps.checkedKeys)
|
||||
keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys)
|
||||
|
||||
// Do nothing if is checkStrictly mode
|
||||
if (checkStrictly) {
|
||||
return keyProps
|
||||
return keyProps
|
||||
}
|
||||
|
||||
/**
|
||||
* Conduct check state by the keyList. It will conduct up & from the provided key.
|
||||
* If the conduct path reach the disabled or already checked / unchecked node will stop conduct.
|
||||
* @param keyList list of keys
|
||||
* @param isCheck is check the node or not
|
||||
* @param keyEntities parsed by `convertTreeToEntities` function in Tree
|
||||
* @param checkStatus Can pass current checked status for process (usually for uncheck operation)
|
||||
* @returns {{checkedKeys: [], halfCheckedKeys: []}}
|
||||
*/
|
||||
export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) {
|
||||
const checkedKeys = {}
|
||||
const halfCheckedKeys = {}; // Record the key has some child checked (include child half checked)
|
||||
|
||||
(checkStatus.checkedKeys || []).forEach((key) => {
|
||||
checkedKeys[key] = true
|
||||
});
|
||||
|
||||
(checkStatus.halfCheckedKeys || []).forEach((key) => {
|
||||
halfCheckedKeys[key] = true
|
||||
})
|
||||
|
||||
// Conduct up
|
||||
function conductUp (key) {
|
||||
if (checkedKeys[key] === isCheck) return
|
||||
|
||||
const entity = keyEntities[key]
|
||||
if (!entity) return
|
||||
|
||||
const { children, parent, node } = entity
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
// Check child node checked status
|
||||
let everyChildChecked = true
|
||||
let someChildChecked = false; // Child checked or half checked
|
||||
|
||||
(children || [])
|
||||
.filter(child => !isCheckDisabled(child.node))
|
||||
.forEach(({ key: childKey }) => {
|
||||
const childChecked = checkedKeys[childKey]
|
||||
const childHalfChecked = halfCheckedKeys[childKey]
|
||||
|
||||
if (childChecked || childHalfChecked) someChildChecked = true
|
||||
if (!childChecked) everyChildChecked = false
|
||||
})
|
||||
|
||||
// Update checked status
|
||||
if (isCheck) {
|
||||
checkedKeys[key] = everyChildChecked
|
||||
} else {
|
||||
checkedKeys[key] = false
|
||||
}
|
||||
halfCheckedKeys[key] = someChildChecked
|
||||
|
||||
if (parent) {
|
||||
conductUp(parent.key)
|
||||
}
|
||||
}
|
||||
|
||||
// Conduct calculate the check status
|
||||
const { checkedKeys = [] } = keyProps
|
||||
return calcCheckStateConduct(children, checkedKeys)
|
||||
// Conduct down
|
||||
function conductDown (key) {
|
||||
if (checkedKeys[key] === isCheck) return
|
||||
|
||||
const entity = keyEntities[key]
|
||||
if (!entity) return
|
||||
|
||||
const { children, node } = entity
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
checkedKeys[key] = isCheck;
|
||||
|
||||
(children || []).forEach((child) => {
|
||||
conductDown(child.key)
|
||||
})
|
||||
}
|
||||
|
||||
function conduct (key) {
|
||||
const entity = keyEntities[key]
|
||||
|
||||
if (!entity) {
|
||||
warning(false, `'${key}' does not exist in the tree.`)
|
||||
return
|
||||
}
|
||||
const { children, parent, node } = entity
|
||||
checkedKeys[key] = isCheck
|
||||
|
||||
if (isCheckDisabled(node)) return;
|
||||
|
||||
// Conduct down
|
||||
(children || [])
|
||||
.filter(child => !isCheckDisabled(child.node))
|
||||
.forEach((child) => {
|
||||
conductDown(child.key)
|
||||
})
|
||||
|
||||
// Conduct up
|
||||
if (parent) {
|
||||
conductUp(parent.key)
|
||||
}
|
||||
}
|
||||
|
||||
(keyList || []).forEach((key) => {
|
||||
conduct(key)
|
||||
})
|
||||
|
||||
const checkedKeyList = []
|
||||
const halfCheckedKeyList = []
|
||||
|
||||
// Fill checked list
|
||||
Object.keys(checkedKeys).forEach((key) => {
|
||||
if (checkedKeys[key]) {
|
||||
checkedKeyList.push(key)
|
||||
}
|
||||
})
|
||||
|
||||
// Fill half checked list
|
||||
Object.keys(halfCheckedKeys).forEach((key) => {
|
||||
if (!checkedKeys[key] && halfCheckedKeys[key]) {
|
||||
halfCheckedKeyList.push(key)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
checkedKeys: checkedKeyList,
|
||||
halfCheckedKeys: halfCheckedKeyList,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If user use `autoExpandParent` we should get the list of parent node
|
||||
* @param keyList
|
||||
* @param keyEntities
|
||||
*/
|
||||
export function conductExpandParent (keyList, keyEntities) {
|
||||
const expandedKeys = {}
|
||||
|
||||
function conductUp (key) {
|
||||
if (expandedKeys[key]) return
|
||||
|
||||
const entity = keyEntities[key]
|
||||
if (!entity) return
|
||||
|
||||
expandedKeys[key] = true
|
||||
|
||||
const { parent, node } = entity
|
||||
|
||||
if (isCheckDisabled(node)) return
|
||||
|
||||
if (parent) {
|
||||
conductUp(parent.key)
|
||||
}
|
||||
}
|
||||
|
||||
(keyList || []).forEach((key) => {
|
||||
conductUp(key)
|
||||
})
|
||||
|
||||
return Object.keys(expandedKeys)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the data- and aria- key/value pairs
|
||||
* @param {object} props
|
||||
*/
|
||||
export function getDataAndAria (props) {
|
||||
return Object.keys(props).reduce((prev, key) => {
|
||||
if ((key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-')) {
|
||||
prev[key] = props[key]
|
||||
}
|
||||
return prev
|
||||
}, {})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue