mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-12-02 12:07:54 +08:00
parent
ff7a426d55
commit
e23265c74c
@ -1,4 +1,3 @@
|
||||
import { getOptionProps } from './props-util'
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
@ -17,9 +16,9 @@ export default {
|
||||
methods: {
|
||||
setState (state, callback) {
|
||||
const newState = typeof state === 'function' ? state(this.$data) : state
|
||||
if (this.getDerivedStateFromProps) {
|
||||
Object.assign(newState, this.getDerivedStateFromProps(getOptionProps(this), this.$data, true) || {})
|
||||
}
|
||||
// if (this.getDerivedStateFromProps) {
|
||||
// Object.assign(newState, this.getDerivedStateFromProps(getOptionProps(this), { ...this.$data, ...newState }, true) || {})
|
||||
// }
|
||||
Object.assign(this.$data, newState)
|
||||
this.$nextTick(() => {
|
||||
callback && callback()
|
||||
|
@ -1,25 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/tree-select/demo/basic.md correctly 1`] = `
|
||||
<span class="ant-select ant-select-enabled ant-select-allow-clear" style="width: 300px;"><span role="combobox" aria-autocomplete="list" aria-haspopup="true" tabindex="0" class="ant-select-selection
|
||||
ant-select-selection--single"><span class="ant-select-selection__rendered"><span class="ant-select-selection__placeholder">Please select</span></span><span class="ant-select-arrow" style="outline: none;"><i class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></span></span>
|
||||
`;
|
||||
exports[`renders ./components/tree-select/demo/basic.md correctly 1`] = `<span role="combobox" aria-haspopup="listbox" tabindex="0" class="ant-select ant-select-enabled ant-select-allow-clear" style="width: 300px;"><span class="ant-select-selection ant-select-selection--single"><span class="ant-select-selection__rendered"><span class="ant-select-selection__placeholder">Please select</span></span><span class="ant-select-arrow" style="outline: none;"><i class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></span></span>`;
|
||||
|
||||
exports[`renders ./components/tree-select/demo/checkable.md correctly 1`] = `
|
||||
<span class="ant-select ant-select-enabled" style="width: 300px;"><span role="combobox" aria-autocomplete="list" aria-haspopup="true" class="ant-select-selection
|
||||
ant-select-selection--multiple"><div class="ant-select-selection__rendered"><li title="Node1" unselectable="unselectable" class="ant-select-selection__choice"><span class="ant-select-selection__choice__remove"><i class="ant-select-remove-icon anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></i></span><span class="ant-select-selection__choice__content">Node1</span></li>
|
||||
<li class="ant-select-search ant-select-search--inline"><span class="ant-select-search__field__wrap"><input role="textbox" class="ant-select-search__field"><span class="ant-select-search__field__mirror"> </span></span></li>
|
||||
</div><span class="ant-select-search__field__placeholder" style="display: none;">Please select</span></span>
|
||||
</span>
|
||||
<span role="combobox" aria-haspopup="listbox" tabindex="-1" class="ant-select ant-select-enabled" style="width: 300px;"><span class="ant-select-selection ant-select-selection--multiple"><div class="ant-select-selection__rendered"><li unselectable="unselectable" role="menuitem" title="Node1" class="ant-select-selection__choice"><span class="ant-select-selection__choice__remove"><i class="ant-select-remove-icon anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></i></span><span class="ant-select-selection__choice__content">Node1</span></li>
|
||||
<li class="ant-select-search ant-select-search--inline"><span class="ant-select-search__field__wrap"><input type="text" aria-label="filter select" aria-autocomplete="list" aria-multiline="false" class="ant-select-search__field" style="width: 0px;"><span class="ant-select-search__field__mirror"> </span></span></li>
|
||||
</div>
|
||||
</span></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tree-select/demo/multiple.md correctly 1`] = `
|
||||
<span class="ant-select ant-select-enabled ant-select-allow-clear" style="width: 300px;"><span role="combobox" aria-autocomplete="list" aria-haspopup="true" class="ant-select-selection
|
||||
ant-select-selection--multiple"><div class="ant-select-selection__rendered"><li class="ant-select-search ant-select-search--inline"><span class="ant-select-search__field__wrap"><input role="textbox" class="ant-select-search__field"><span class="ant-select-search__field__mirror"> </span></span></li>
|
||||
<span role="combobox" aria-haspopup="listbox" tabindex="-1" class="ant-select ant-select-enabled ant-select-allow-clear" style="width: 300px;"><span class="ant-select-selection ant-select-selection--multiple"><div class="ant-select-selection__rendered"><li class="ant-select-search ant-select-search--inline"><span class="ant-select-search__field__wrap"><input type="text" aria-label="filter select" aria-autocomplete="list" aria-multiline="false" class="ant-select-search__field" style="width: 0px;"><span class="ant-select-search__field__mirror"> </span></span></li>
|
||||
</div><span class="ant-select-search__field__placeholder" style="display: block;">Please select</span></span></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = `
|
||||
<span class="ant-select ant-select-enabled" style="width: 300px;"><span role="combobox" aria-autocomplete="list" aria-haspopup="true" tabindex="0" class="ant-select-selection
|
||||
ant-select-selection--single"><span class="ant-select-selection__rendered"><span class="ant-select-selection__placeholder">Please select</span></span><span class="ant-select-arrow" style="outline: none;"><i class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></span></span>
|
||||
`;
|
||||
exports[`renders ./components/tree-select/demo/suffix.md correctly 1`] = `<span role="combobox" aria-haspopup="listbox" tabindex="0" class="ant-select ant-select-enabled ant-select-allow-clear" style="width: 300px;"><span class="ant-select-selection ant-select-selection--single"><span class="ant-select-selection__rendered"><span class="ant-select-selection__placeholder">Please select</span></span><span class="ant-select-arrow" style="outline: none;"><i class="anticon anticon-smile"><svg viewBox="64 64 896 896" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"></path></svg></i></span></span></span>`;
|
||||
|
||||
exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = `<span role="combobox" aria-haspopup="listbox" tabindex="0" class="ant-select ant-select-enabled" style="width: 300px;"><span class="ant-select-selection ant-select-selection--single"><span class="ant-select-selection__rendered"><span class="ant-select-selection__placeholder">Please select</span></span><span class="ant-select-arrow" style="outline: none;"><i class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span></span></span>`;
|
||||
|
@ -22,7 +22,7 @@ The most basic usage.
|
||||
>
|
||||
<a-tree-select-node value='parent 1' title='parent 1' key='0-1'>
|
||||
<a-tree-select-node value='parent 1-0' title='parent 1-0' key='0-1-1'>
|
||||
<a-tree-select-node value='leaf1' title='my leaf' key='random' />
|
||||
<a-tree-select-node :selectable="false" value='leaf1' title='my leaf' key='random' />
|
||||
<a-tree-select-node value='leaf2' title='your leaf' key='random1' />
|
||||
</a-tree-select-node>
|
||||
<a-tree-select-node value='parent 1-1' title='parent 1-1' key='random2'>
|
||||
@ -39,12 +39,13 @@ The most basic usage.
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
treeExpandedKeys: [],
|
||||
value: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange (value) {
|
||||
console.log(arguments)
|
||||
console.log(value)
|
||||
this.value = value
|
||||
},
|
||||
},
|
||||
|
@ -18,7 +18,6 @@ Multiple and checkable.
|
||||
treeCheckable
|
||||
:showCheckedStrategy="SHOW_PARENT"
|
||||
searchPlaceholder='Please select'
|
||||
treeNodeFilterProp='label'
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -27,29 +26,29 @@ import { TreeSelect } from 'ant-design-vue'
|
||||
const SHOW_PARENT = TreeSelect.SHOW_PARENT
|
||||
|
||||
const treeData = [{
|
||||
label: 'Node1',
|
||||
title: 'Node1',
|
||||
value: '0-0',
|
||||
key: '0-0',
|
||||
children: [{
|
||||
label: 'Child Node1',
|
||||
title: 'Child Node1',
|
||||
value: '0-0-0',
|
||||
key: '0-0-0',
|
||||
}],
|
||||
}, {
|
||||
label: 'Node2',
|
||||
title: 'Node2',
|
||||
value: '0-1',
|
||||
key: '0-1',
|
||||
children: [{
|
||||
label: 'Child Node3',
|
||||
title: 'Child Node3',
|
||||
value: '0-1-0',
|
||||
key: '0-1-0',
|
||||
disabled: true,
|
||||
}, {
|
||||
label: 'Child Node4',
|
||||
title: 'Child Node4',
|
||||
value: '0-1-1',
|
||||
key: '0-1-1',
|
||||
}, {
|
||||
label: 'Child Node5',
|
||||
title: 'Child Node5',
|
||||
value: '0-1-2',
|
||||
key: '0-1-2',
|
||||
}],
|
||||
@ -64,7 +63,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onChange (value) {
|
||||
console.log('onChange ', value, arguments)
|
||||
console.log('onChange ', value)
|
||||
this.value = value
|
||||
},
|
||||
},
|
||||
|
@ -3,6 +3,7 @@ import Basic from './basic'
|
||||
import Checkable from './checkable'
|
||||
import Multiple from './multiple'
|
||||
import TreeData from './treeData'
|
||||
import Suffix from './suffix'
|
||||
|
||||
import CN from '../index.zh-CN.md'
|
||||
import US from '../index.en-US.md'
|
||||
@ -35,6 +36,7 @@ export default {
|
||||
<Checkable/>
|
||||
<Multiple/>
|
||||
<TreeData/>
|
||||
<Suffix />
|
||||
<api>
|
||||
<template slot='cn'>
|
||||
<CN/>
|
||||
|
@ -47,14 +47,14 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onChange (value) {
|
||||
console.log(arguments)
|
||||
console.log(value)
|
||||
this.value = value
|
||||
},
|
||||
onSearch () {
|
||||
console.log(arguments)
|
||||
console.log(...arguments)
|
||||
},
|
||||
onSelect () {
|
||||
console.log(arguments)
|
||||
console.log(...arguments)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
54
components/tree-select/demo/suffix.md
Normal file
54
components/tree-select/demo/suffix.md
Normal file
@ -0,0 +1,54 @@
|
||||
<cn>
|
||||
#### 后缀图标
|
||||
最简单的用法。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Suffix
|
||||
The most basic usage.
|
||||
</us>
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-tree-select
|
||||
showSearch
|
||||
style="width: 300px"
|
||||
:value="value"
|
||||
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
placeholder='Please select'
|
||||
allowClear
|
||||
treeDefaultExpandAll
|
||||
@change="onChange"
|
||||
>
|
||||
<a-icon slot="suffixIcon" type="smile" />
|
||||
<a-tree-select-node value='parent 1' title='parent 1' key='0-1'>
|
||||
<a-tree-select-node value='parent 1-0' title='parent 1-0' key='0-1-1'>
|
||||
<a-tree-select-node value='leaf1' title='my leaf' key='random' />
|
||||
<a-tree-select-node value='leaf2' title='your leaf' key='random1' />
|
||||
</a-tree-select-node>
|
||||
<a-tree-select-node value='parent 1-1' title='parent 1-1' key='random2'>
|
||||
<a-tree-select-node value='sss' key='random3'>
|
||||
<b style="color: #08c" slot="title">sss</b>
|
||||
</a-tree-select-node>
|
||||
</a-tree-select-node>
|
||||
</a-tree-select-node>
|
||||
</a-tree-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
value: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange (value) {
|
||||
console.log(value)
|
||||
this.value = value
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
@ -16,10 +16,9 @@ The tree structure can be populated using `treeData` property. This is a quick a
|
||||
:treeData="treeData"
|
||||
placeholder='Please select'
|
||||
treeDefaultExpandAll
|
||||
labelInValue
|
||||
v-model="value"
|
||||
>
|
||||
<span style="color: #08c" slot="label" slot-scope="{key, value}" v-if="key='0-0-1'">
|
||||
<span style="color: #08c" slot="title" slot-scope="{key, value}" v-if="key='0-0-1'">
|
||||
<a-icon type="home"/>Child Node1 {{value}}
|
||||
</span>
|
||||
</a-tree-select>
|
||||
@ -27,22 +26,22 @@ The tree structure can be populated using `treeData` property. This is a quick a
|
||||
|
||||
<script>
|
||||
const treeData = [{
|
||||
label: 'Node1',
|
||||
title: 'Node1',
|
||||
value: '0-0',
|
||||
key: '0-0',
|
||||
children: [{
|
||||
value: '0-0-1',
|
||||
key: '0-0-1',
|
||||
scopedSlots: { // custom label
|
||||
label: 'label',
|
||||
scopedSlots: { // custom title
|
||||
title: 'title',
|
||||
},
|
||||
}, {
|
||||
label: 'Child Node2',
|
||||
title: 'Child Node2',
|
||||
value: '0-0-2',
|
||||
key: '0-0-2',
|
||||
}],
|
||||
}, {
|
||||
label: 'Node2',
|
||||
title: 'Node2',
|
||||
value: '0-1',
|
||||
key: '0-1',
|
||||
}]
|
||||
|
@ -21,16 +21,17 @@
|
||||
| showCheckedStrategy | The way show selected item in box. **Default:** just show child nodes. **`TreeSelect.SHOW_ALL`:** show all checked treeNodes (include parent treeNode). **`TreeSelect.SHOW_PARENT`:** show checked treeNodes (just show parent treeNode). | enum { TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
|
||||
| showSearch | Whether to display a search input in the dropdown menu(valid only in the single mode) | boolean | false |
|
||||
| size | To set the size of the select input, options: `large` `small` | string | 'default' |
|
||||
| suffixIcon | The custom suffix icon | VNode \| slot | - |
|
||||
| treeCheckable | Whether to show checkbox on the treeNodes | boolean | false |
|
||||
| treeCheckStrictly | Whether to check nodes precisely (in the `checkable` mode), means parent and child nodes are not associated, and it will make `labelInValue` be true | boolean | false |
|
||||
| treeData | Data of the treeNodes, manual construction work is no longer needed if this property has been set(ensure the Uniqueness of each value) | array<{ value, label, children, [disabled, disableCheckbox, selectable] }> | \[] |
|
||||
| treeDataSimpleMode | Enable simple mode of treeData.(treeData should like this: [{id:1, pId:0, value:'1', label:"test1",...},...], pId is parent node's id) | false\|Array<{ id: string, pId: string, rootPId: null }> | false |
|
||||
| treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false |
|
||||
| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] | - |
|
||||
| treeExpandedKeys | Set expanded keys | string\[] | - |
|
||||
| treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | 'value' |
|
||||
| treeNodeLabelProp | Will render as content of select | string | 'title' |
|
||||
| value(v-model) | To set the current selected treeNode(s). | string\|string\[] | - |
|
||||
| suffixIcon | The custom suffix icon | VNode \| slot | - |
|
||||
|
||||
### Events
|
||||
| Events Name | Description | Arguments |
|
||||
@ -38,6 +39,7 @@
|
||||
| change | A callback function, can be executed when selected treeNodes or input value change | function(value, label, extra) |
|
||||
| search | A callback function, can be executed when the search input changes. | function(value: string) |
|
||||
| select | A callback function, can be executed when you select a treeNode. | function(value, node, extra) |
|
||||
| treeExpand | A callback function, can be executed when treeNode expanded | function(expandedKeys) |
|
||||
|
||||
### Tree Methods
|
||||
|
||||
@ -52,6 +54,7 @@
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| selectable | can be selected | boolean | true |
|
||||
| disableCheckbox | Disables the checkbox of the treeNode | boolean | false |
|
||||
| disabled | Disabled or not | boolean | false |
|
||||
| isLeaf | Leaf node or not | boolean | false |
|
||||
|
@ -136,10 +136,12 @@ const TreeSelect = {
|
||||
dropdownStyle: { maxHeight: '100vh', overflow: 'auto', ...dropdownStyle },
|
||||
treeCheckable: checkable,
|
||||
notFoundContent: notFoundContent || locale.notFoundContent,
|
||||
__propsSymbol__: Symbol(),
|
||||
},
|
||||
class: cls,
|
||||
on: { ...this.$listeners, change: this.onChange },
|
||||
ref: 'vcTreeSelect',
|
||||
scopedSlots: this.$scopedSlots,
|
||||
}
|
||||
return (
|
||||
<VcTreeSelect {...VcTreeSelectProps}>{filterEmpty(this.$slots.default)}</VcTreeSelect>
|
||||
|
@ -21,24 +21,26 @@
|
||||
| showCheckedStrategy | 定义选中项回填的方式。`TreeSelect.SHOW_ALL`: 显示所有选中节点(包括父节点). `TreeSelect.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时). 默认只显示子节点. | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
|
||||
| showSearch | 在下拉中显示搜索框(仅在单选模式下生效) | boolean | false |
|
||||
| size | 选择框大小,可选 `large` `small` | string | 'default' |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - |
|
||||
| treeCheckable | 显示 checkbox | boolean | false |
|
||||
| treeCheckStrictly | checkable 状态下节点选择完全受控(父子节点选中状态不再关联),会使得 `labelInValue` 强制为 true | boolean | false |
|
||||
| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(value 在整个树范围内唯一) | array<{value, label, children, [disabled, disableCheckbox, selectable]}> | \[] |
|
||||
| treeDataSimpleMode | 使用简单格式的 treeData,具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: [{id:1, pId:0, value:'1', label:"test1",...},...], `pId` 是父节点的 id) | false\|Array<{ id: string, pId: string, rootPId: null }> | false |
|
||||
| treeDefaultExpandAll | 默认展开所有树节点 | boolean | false |
|
||||
| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] | - |
|
||||
| treeExpandedKeys | 设置展开的树节点 | string\[] | - |
|
||||
| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' |
|
||||
| treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' |
|
||||
| value(v-model) | 指定当前选中的条目 | string/string\[] | - |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| change | 选中树节点时调用此函数 | function(value, label, extra) | - |
|
||||
| search | 文本框值变化时回调 | function(value: string) | - |
|
||||
| select | 被选中时调用 | function(value, node, extra) | - |
|
||||
| change | 选中树节点时调用此函数 | function(value, label, extra) |
|
||||
| search | 文本框值变化时回调 | function(value: string) |
|
||||
| select | 被选中时调用 | function(value, node, extra) |
|
||||
| treeExpand | 展开节点时调用 | function(expandedKeys) |
|
||||
|
||||
### Tree 方法
|
||||
|
||||
@ -53,6 +55,7 @@
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| selectable | 是否可选 | boolean | true |
|
||||
| disableCheckbox | 禁掉 checkbox | boolean | false |
|
||||
| disabled | 是否禁用 | boolean | false |
|
||||
| isLeaf | 是否是叶子节点 | boolean | false |
|
||||
|
@ -11,6 +11,14 @@ export const TreeData = PropTypes.shape({
|
||||
|
||||
export const TreeSelectProps = () => ({
|
||||
...AbstractSelectProps(),
|
||||
autoFocus: PropTypes.bool,
|
||||
dropdownStyle: PropTypes.object,
|
||||
filterTreeNode: PropTypes.oneOfType([Function, Boolean]),
|
||||
getPopupContainer: PropTypes.func,
|
||||
labelInValue: PropTypes.bool,
|
||||
loadData: PropTypes.func,
|
||||
maxTagCount: PropTypes.number,
|
||||
maxTagPlaceholder: PropTypes.any,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.array]),
|
||||
defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
multiple: PropTypes.bool,
|
||||
@ -18,21 +26,18 @@ export const TreeSelectProps = () => ({
|
||||
// onChange: (value: any, label: any) => void,
|
||||
// onSearch: (value: any) => void,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
dropdownClassName: PropTypes.string,
|
||||
dropdownStyle: PropTypes.object,
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
treeDefaultExpandAll: PropTypes.bool,
|
||||
showCheckedStrategy: PropTypes.oneOf(['SHOW_ALL', 'SHOW_PARENT', 'SHOW_CHILD']),
|
||||
suffixIcon: PropTypes.any,
|
||||
treeCheckable: PropTypes.bool,
|
||||
treeDefaultExpandedKeys: PropTypes.arrayOf(String),
|
||||
filterTreeNode: PropTypes.func,
|
||||
treeNodeFilterProp: PropTypes.string,
|
||||
treeNodeLabelProp: PropTypes.string,
|
||||
treeCheckStrictly: PropTypes.bool,
|
||||
treeData: PropTypes.arrayOf(Object),
|
||||
treeDataSimpleMode: PropTypes.oneOfType([Boolean, Object]),
|
||||
loadData: PropTypes.func,
|
||||
showCheckedStrategy: PropTypes.oneOf(['SHOW_ALL', 'SHOW_PARENT', 'SHOW_CHILD']),
|
||||
labelInValue: PropTypes.bool,
|
||||
treeCheckStrictly: PropTypes.bool,
|
||||
getPopupContainer: PropTypes.func,
|
||||
suffixIcon: PropTypes.any,
|
||||
|
||||
dropdownClassName: PropTypes.string,
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
treeDefaultExpandAll: PropTypes.bool,
|
||||
treeExpandedKeys: PropTypes.arrayOf(String),
|
||||
treeDefaultExpandedKeys: PropTypes.arrayOf(String),
|
||||
treeNodeFilterProp: PropTypes.string,
|
||||
treeNodeLabelProp: PropTypes.string,
|
||||
})
|
||||
|
@ -2,7 +2,7 @@ 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'
|
||||
import { initDefaultProps, getOptionProps, filterEmpty } from '../_util/props-util'
|
||||
import Icon from '../icon'
|
||||
|
||||
function TreeProps () {
|
||||
@ -179,7 +179,7 @@ export default {
|
||||
props: {
|
||||
...props,
|
||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||
children: this.$slots.default || [],
|
||||
children: filterEmpty(this.$slots.default || []),
|
||||
__propsSymbol__: Symbol(),
|
||||
switcherIcon: this.renderSwitcherIcon,
|
||||
},
|
||||
|
@ -107,7 +107,7 @@ const Select = {
|
||||
this.__propsSymbol__,
|
||||
'Replace slots.default with props.children and pass props.__propsSymbol__'
|
||||
)
|
||||
return {
|
||||
const state = {
|
||||
_value: this.getValueFromProps(props, true), // true: use default value
|
||||
_inputValue: props.combobox ? this.getInputValueForCombobox(
|
||||
props,
|
||||
@ -119,10 +119,10 @@ const Select = {
|
||||
// a flag for aviod redundant getOptionsInfoFromProps call
|
||||
_skipBuildOptionsInfo: true,
|
||||
}
|
||||
},
|
||||
beforeMount () {
|
||||
const state = this.getDerivedStateFromProps(getOptionProps(this), this.$data)
|
||||
Object.assign(this.$data, state)
|
||||
return {
|
||||
...state,
|
||||
...this.getDerivedStateFromProps(props, state),
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
|
@ -37,7 +37,8 @@
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
width: 20px;
|
||||
b {
|
||||
&:after {
|
||||
content: '';
|
||||
border-color: #999999 transparent transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px 4px 0 4px;
|
||||
@ -66,22 +67,30 @@
|
||||
&__clear {
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '×'
|
||||
&-enabled {
|
||||
.@{selectPrefixCls}-selection {
|
||||
&:hover {
|
||||
border-color: #23c0fa;
|
||||
box-shadow: 0 0 2px fadeout(#2db7f5, 20%);
|
||||
}
|
||||
&:active {
|
||||
border-color: #2db7f5;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{selectPrefixCls}-focused {
|
||||
.@{selectPrefixCls}-selection {
|
||||
//border-color: #23c0fa;
|
||||
border-color: #7700fa;
|
||||
box-shadow: 0 0 2px fadeout(#2db7f5, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-enabled &-selection {
|
||||
&:hover {
|
||||
border-color: #23c0fa;
|
||||
box-shadow: 0 0 2px fadeout(#2db7f5, 20%);
|
||||
}
|
||||
&:active {
|
||||
border-color: #2db7f5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&-selection--single {
|
||||
height: 28px;
|
||||
@ -105,6 +114,9 @@
|
||||
.@{selectPrefixCls}-selection__clear {
|
||||
top: 5px;
|
||||
right: 20px;
|
||||
&:after {
|
||||
content: '×';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +129,7 @@
|
||||
cursor: not-allowed;
|
||||
color: #ccc;
|
||||
|
||||
&:hover {
|
||||
&:hover{
|
||||
cursor: not-allowed;
|
||||
color: #ccc;
|
||||
}
|
||||
@ -130,6 +142,7 @@
|
||||
}
|
||||
|
||||
&-search__field__placeholder {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 3px;
|
||||
@ -288,6 +301,7 @@
|
||||
top: 0;
|
||||
right: 2px;
|
||||
transition: opacity .3s, transform .3s;
|
||||
|
||||
&:before {
|
||||
content: '×'
|
||||
}
|
||||
@ -487,7 +501,7 @@
|
||||
}
|
||||
|
||||
&-open {
|
||||
.@{selectPrefixCls}-arrow b {
|
||||
.@{selectPrefixCls}-arrow:after {
|
||||
border-color: transparent transparent #888 transparent;
|
||||
border-width: 0 4px 5px 4px;
|
||||
}
|
||||
@ -498,3 +512,25 @@
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-icon-demo {
|
||||
.@{selectPrefixCls} {
|
||||
&-selection__choice__remove {
|
||||
&:before {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-selection__clear {
|
||||
&:after {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
}
|
||||
}
|
||||
&.filter-node {
|
||||
> a {
|
||||
> .@{treePrefixCls}-node-content-wrapper {
|
||||
color: #a60000!important;
|
||||
font-weight: bold!important;
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
/* eslint react/no-multi-comp:0, no-console:0, no-alert: 0 */
|
||||
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import '../assets/index.less'
|
||||
import './demo.less'
|
||||
|
||||
import '../../vc-dialog/assets/index.less'
|
||||
import Dialog from '../../vc-dialog'
|
||||
import TreeSelect, { TreeNode, SHOW_PARENT } from '../index'
|
||||
import TreeSelect, { TreeNode, SHOW_PARENT } from '../src/index'
|
||||
import { gData } from './util'
|
||||
import './demo.less'
|
||||
|
||||
function isLeaf (value) {
|
||||
if (!value) {
|
||||
@ -51,89 +50,90 @@ function findPath (value, data) {
|
||||
}
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
tsOpen: false,
|
||||
visible: false,
|
||||
inputValue: '0-0-0-label',
|
||||
value: '0-0-0-value1',
|
||||
// value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
|
||||
lv: { value: '0-0-0-value', label: 'spe label' },
|
||||
multipleValue: [],
|
||||
simpleTreeData: [
|
||||
{ key: 1, pId: 0, label: 'test1', value: 'test1' },
|
||||
{ key: 121, pId: 0, label: 'test1', value: 'test121' },
|
||||
{ key: 11, pId: 1, label: 'test11', value: 'test11' },
|
||||
{ key: 12, pId: 1, label: 'test12', value: 'test12' },
|
||||
{ key: 111, pId: 11, label: 'test111', value: 'test111' },
|
||||
],
|
||||
treeDataSimpleMode: {
|
||||
id: 'key',
|
||||
rootPId: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
// console.log(this.refs.mul.getInputDOMNode());
|
||||
// this.refs.mul.getInputDOMNode().setAttribute('disabled', true);
|
||||
},
|
||||
mixins: [BaseMixin],
|
||||
data: () => ({
|
||||
tsOpen: false,
|
||||
visible: false,
|
||||
searchValue: '0-0-0-label',
|
||||
value: '0-0-0-value1',
|
||||
// value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
|
||||
lv: { value: '0-0-0-value', label: 'spe label' },
|
||||
multipleValue: [],
|
||||
simpleSearchValue: 'test111',
|
||||
simpleTreeData: [
|
||||
{ key: 1, pId: 0, label: 'test1', value: 'test1' },
|
||||
{ key: 121, pId: 0, label: 'test2', value: 'test2' },
|
||||
{ key: 11, pId: 1, label: 'test11', value: 'test11' },
|
||||
{ key: 12, pId: 1, label: 'test12', value: 'test12' },
|
||||
{ key: 111, pId: 11, label: 'test111', value: 'test111' },
|
||||
],
|
||||
treeDataSimpleMode: {
|
||||
id: 'key',
|
||||
rootPId: 0,
|
||||
},
|
||||
}),
|
||||
methods: {
|
||||
onClick () {
|
||||
this.visible = true
|
||||
onClick () {
|
||||
this.setState({
|
||||
visible: true,
|
||||
})
|
||||
},
|
||||
|
||||
onClose () {
|
||||
this.visible = false
|
||||
onClose () {
|
||||
this.setState({
|
||||
visible: false,
|
||||
})
|
||||
},
|
||||
|
||||
onSearch (value) {
|
||||
console.log(value, arguments)
|
||||
console.log('Do Search:', value, arguments)
|
||||
this.setState({ searchValue: value })
|
||||
},
|
||||
|
||||
onChange (value) {
|
||||
console.log('onChange', arguments)
|
||||
this.value = value
|
||||
onChange (value, ...rest) {
|
||||
console.log('onChange', value, ...rest)
|
||||
this.setState({ value })
|
||||
},
|
||||
|
||||
onChangeChildren (value) {
|
||||
console.log('onChangeChildren', arguments)
|
||||
onChangeChildren (...args) {
|
||||
console.log('onChangeChildren', ...args)
|
||||
const value = args[0]
|
||||
const pre = value ? this.value : undefined
|
||||
this.value = isLeaf(value) ? value : pre
|
||||
this.setState({ value: isLeaf(value) ? value : pre })
|
||||
},
|
||||
|
||||
onChangeLV (value) {
|
||||
onChangeLV (value) {
|
||||
console.log('labelInValue', arguments)
|
||||
if (!value) {
|
||||
this.lv = undefined
|
||||
this.setState({ lv: undefined })
|
||||
return
|
||||
}
|
||||
const path = findPath(value.value, gData).map(i => i.label).reverse().join(' > ')
|
||||
this.lv = { value: value.value, label: path }
|
||||
this.setState({ lv: { value: value.value, label: path }})
|
||||
},
|
||||
|
||||
onMultipleChange (value) {
|
||||
console.log('onMultipleChange', arguments)
|
||||
this.multipleValue = value
|
||||
this.setState({ multipleValue: value })
|
||||
},
|
||||
|
||||
onSelect () {
|
||||
// use onChange instead
|
||||
console.log(...arguments)
|
||||
console.log(arguments)
|
||||
},
|
||||
|
||||
onDropdownVisibleChange (visible, info) {
|
||||
onDropdownVisibleChange (visible, info) {
|
||||
console.log(visible, this.value, info)
|
||||
if (Array.isArray(this.value) && this.value.length > 1 &&
|
||||
this.value.length < 3) {
|
||||
alert('please select more than two item or less than one item.')
|
||||
window.alert('please select more than two item or less than one item.')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
filterTreeNode (input, child) {
|
||||
return String(child.title).indexOf(input) === 0
|
||||
filterTreeNode (input, child) {
|
||||
return String(child.data.props.title).indexOf(input) === 0
|
||||
},
|
||||
},
|
||||
|
||||
@ -167,10 +167,10 @@ export default {
|
||||
onSearch={this.onSearch}
|
||||
onChange={this.onChange}
|
||||
onSelect={this.onSelect}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
</div>
|
||||
</Dialog> : null}
|
||||
|
||||
<h2>single select</h2>
|
||||
<TreeSelect
|
||||
style={{ width: '300px' }}
|
||||
@ -180,31 +180,35 @@ export default {
|
||||
placeholder={<i>请下拉选择</i>}
|
||||
searchPlaceholder='please search'
|
||||
showSearch allowClear treeLine
|
||||
inputValue={this.inputValue}
|
||||
searchValue={this.searchValue}
|
||||
value={this.value}
|
||||
treeData={gData}
|
||||
treeNodeFilterProp='label'
|
||||
filterTreeNode={false}
|
||||
onSearch={this.onSearch}
|
||||
open={this.tsOpen}
|
||||
onChange={(value) => {
|
||||
console.log('onChange', value, arguments)
|
||||
onChange={(value, ...args) => {
|
||||
console.log('onChange', value, ...args)
|
||||
if (value === '0-0-0-0-value') {
|
||||
this.tsOpen = true
|
||||
this.setState({ tsOpen: true })
|
||||
} else {
|
||||
this.tsOpen = false
|
||||
this.setState({ tsOpen: false })
|
||||
}
|
||||
this.value = value
|
||||
this.setState({ value })
|
||||
} }
|
||||
dropdownVisibleChange={(v, info) => {
|
||||
console.log('single dropdownVisibleChange', v, info)
|
||||
console.log('single onDropdownVisibleChange', v, info)
|
||||
// document clicked
|
||||
if (info.documentClickClose && this.value === '0-0-0-0-value') {
|
||||
return false
|
||||
}
|
||||
this.setState({
|
||||
tsOpen: v,
|
||||
})
|
||||
return true
|
||||
} }
|
||||
onSelect={this.onSelect}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>single select (just select children)</h2>
|
||||
@ -221,10 +225,11 @@ export default {
|
||||
treeNodeFilterProp='label'
|
||||
filterTreeNode={false}
|
||||
onChange={this.onChangeChildren}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>multiple select</h2>
|
||||
<TreeSelect ref='mul'
|
||||
<TreeSelect
|
||||
style={{ width: '300px' }}
|
||||
transitionName='rc-tree-select-dropdown-slide-up'
|
||||
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||
@ -238,6 +243,7 @@ export default {
|
||||
onChange={this.onMultipleChange}
|
||||
onSelect={this.onSelect}
|
||||
allowClear
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>check select</h2>
|
||||
@ -247,17 +253,23 @@ export default {
|
||||
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||
dropdownStyle={{ height: '200px', overflow: 'auto' }}
|
||||
dropdownPopupAlign={{ overflow: { adjustY: 0, adjustX: 0 }, offset: [0, 2] }}
|
||||
onDropdownVisibleChange={this.onDropdownVisibleChange}
|
||||
dropdownVisibleChange={this.onDropdownVisibleChange}
|
||||
placeholder={<i>请下拉选择</i>}
|
||||
searchPlaceholder='please search'
|
||||
treeLine maxTagTextLength={10}
|
||||
value={this.value}
|
||||
inputValue={null}
|
||||
autoClearSearchValue
|
||||
treeData={gData}
|
||||
treeNodeFilterProp='title'
|
||||
treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||
onChange={this.onChange}
|
||||
onSelect={this.onSelect}
|
||||
maxTagCount={2}
|
||||
maxTagPlaceholder={(valueList) => {
|
||||
console.log('Max Tag Rest Value:', valueList)
|
||||
return `${valueList.length} rest...`
|
||||
}}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>labelInValue & show path</h2>
|
||||
@ -274,6 +286,7 @@ export default {
|
||||
treeNodeFilterProp='label'
|
||||
filterTreeNode={false}
|
||||
onChange={this.onChangeLV}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>use treeDataSimpleMode</h2>
|
||||
@ -283,7 +296,10 @@ export default {
|
||||
placeholder={<i>请下拉选择</i>}
|
||||
searchPlaceholder='please search'
|
||||
treeLine maxTagTextLength={10}
|
||||
inputValue={'test111'}
|
||||
searchValue={this.simpleSearchValue}
|
||||
onSearch={(simpleSearchValue) => {
|
||||
this.setState({ simpleSearchValue })
|
||||
}}
|
||||
value={this.value}
|
||||
treeData={this.simpleTreeData}
|
||||
treeNodeFilterProp='title'
|
||||
@ -291,13 +307,14 @@ export default {
|
||||
treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||
onChange={this.onChange}
|
||||
onSelect={this.onSelect}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>Testing in extreme conditions (Boundary conditions test) </h2>
|
||||
<TreeSelect
|
||||
style={{ width: '200px' }}
|
||||
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||
defaultValue={'leaf1'} multiple treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||
defaultValue='leaf1' multiple treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||
treeDefaultExpandAll
|
||||
treeData={[
|
||||
{ key: '', value: '', label: 'empty value', children: [] },
|
||||
@ -308,18 +325,20 @@ export default {
|
||||
],
|
||||
},
|
||||
]}
|
||||
onChange={(val) => console.log(val, arguments)}
|
||||
onChange={(val, ...args) => console.log(val, ...args)}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>use TreeNode Component (not recommend)</h2>
|
||||
<TreeSelect
|
||||
style={{ width: '200px' }}
|
||||
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||
defaultValue={'leaf1'}
|
||||
defaultValue='leaf1'
|
||||
treeDefaultExpandAll
|
||||
treeNodeFilterProp='title'
|
||||
filterTreeNode={this.filterTreeNode}
|
||||
onChange={(val) => console.log(val, arguments)}
|
||||
onChange={(val, ...args) => console.log(val, ...args)}
|
||||
__propsSymbol__={Symbol()}
|
||||
>
|
||||
<TreeNode value='' title='parent 1' key=''>
|
||||
<TreeNode value='parent 1-0' title='parent 1-0' key='0-1-0'>
|
||||
@ -331,7 +350,7 @@ export default {
|
||||
title={<span style={{ color: 'red' }}>sss</span>} key='random3'
|
||||
/>
|
||||
<TreeNode value='same value1' title='same txtle' key='0-1-1-1'>
|
||||
<TreeNode value='same value10' title='same titlexd' key='0-1-1-1-0' />
|
||||
<TreeNode value='same value10' title='same titlexd' key='0-1-1-1-0' style={{ color: 'red', background: 'green' }} />
|
||||
</TreeNode>
|
||||
</TreeNode>
|
||||
</TreeNode>
|
||||
|
@ -56,6 +56,7 @@ export default {
|
||||
treeCheckable
|
||||
showCheckedStrategy={SHOW_PARENT}
|
||||
onChange={this.onChange}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -70,6 +71,7 @@ export default {
|
||||
treeCheckStrictly
|
||||
showCheckedStrategy={SHOW_PARENT}
|
||||
onChange={this.onChangeStrictly}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
68
components/vc-tree-select/demo/controlled.jsx
Normal file
68
components/vc-tree-select/demo/controlled.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
/* eslint react/no-multi-comp:0, no-console:0, no-alert: 0 */
|
||||
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import '../assets/index.less'
|
||||
import '../../vc-dialog/assets/index.less'
|
||||
import TreeSelect, { TreeNode } from '../src/index'
|
||||
import './demo.less'
|
||||
|
||||
export default {
|
||||
mixins: [BaseMixin],
|
||||
data: () => ({
|
||||
treeExpandedKeys: [],
|
||||
}),
|
||||
methods: {
|
||||
onTreeExpand (treeExpandedKeys) {
|
||||
this.setState({
|
||||
treeExpandedKeys,
|
||||
})
|
||||
},
|
||||
|
||||
setTreeExpandedKeys () {
|
||||
this.setState({
|
||||
treeExpandedKeys: ['000', '0-1-0'],
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
render () {
|
||||
const { treeExpandedKeys } = this
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Conrolled treeExpandedKeys</h2>
|
||||
<TreeSelect
|
||||
style={{ width: '200px' }}
|
||||
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||
treeExpandedKeys={treeExpandedKeys}
|
||||
onTreeExpand={this.onTreeExpand}
|
||||
__propsSymbol__={Symbol()}
|
||||
>
|
||||
<TreeNode value='' title='parent 1' key='000'>
|
||||
<TreeNode value='parent 1-0' title='parent 1-0' key='0-1-0'>
|
||||
<TreeNode value='leaf1' title='my leaf' key='random' />
|
||||
<TreeNode value='leaf2' title='your leaf' key='random1' disabled />
|
||||
</TreeNode>
|
||||
<TreeNode value='parent 1-1' title='parent 1-1' key='0-1-1'>
|
||||
<TreeNode value='sss'
|
||||
title={<span style={{ color: 'red' }}>sss</span>} key='random3'
|
||||
/>
|
||||
<TreeNode value='same value1' title='same txtle' key='0-1-1-1'>
|
||||
<TreeNode value='same value10' title='same titlexd' key='0-1-1-1-0' style={{ color: 'red', background: 'green' }} />
|
||||
</TreeNode>
|
||||
</TreeNode>
|
||||
</TreeNode>
|
||||
<TreeNode value='same value2' title='same title' key='0-2'>
|
||||
<TreeNode value='2same value' title='2same title' key='0-2-0' />
|
||||
</TreeNode>
|
||||
<TreeNode value='same value3' title='same title' key='0-3' />
|
||||
</TreeSelect>
|
||||
<button onClick={this.setTreeExpandedKeys}>
|
||||
Set treeExpandedKeys
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
113
components/vc-tree-select/demo/custom-icons.jsx
Normal file
113
components/vc-tree-select/demo/custom-icons.jsx
Normal file
@ -0,0 +1,113 @@
|
||||
/* eslint react/no-multi-comp:0, no-console:0, no-alert: 0 */
|
||||
|
||||
import '../assets/index.less'
|
||||
import '../../vc-dialog/assets/index.less'
|
||||
import TreeSelect from '../src/index'
|
||||
import { gData } from './util'
|
||||
import './demo.less'
|
||||
|
||||
const bubblePath = 'M632 888H392c-4.4 0-8 3.6-8 8v32c0 ' +
|
||||
'17.7 14.3 32 32 32h192c17.7 0 32-14.3 32-32v-3' +
|
||||
'2c0-4.4-3.6-8-8-8zM512 64c-181.1 0-328 146.9-3' +
|
||||
'28 328 0 121.4 66 227.4 164 284.1V792c0 17.7 1' +
|
||||
'4.3 32 32 32h264c17.7 0 32-14.3 32-32V676.1c98' +
|
||||
'-56.7 164-162.7 164-284.1 0-181.1-146.9-328-32' +
|
||||
'8-328z m127.9 549.8L604 634.6V752H420V634.6l-3' +
|
||||
'5.9-20.8C305.4 568.3 256 484.5 256 392c0-141.4' +
|
||||
' 114.6-256 256-256s256 114.6 256 256c0 92.5-49' +
|
||||
'.4 176.3-128.1 221.8z'
|
||||
|
||||
const clearPath = 'M793 242H366v-74c0-6.7-7.7-10.4-12.9' +
|
||||
'-6.3l-142 112c-4.1 3.2-4.1 9.4 0 12.6l142 112c' +
|
||||
'5.2 4.1 12.9 0.4 12.9-6.3v-74h415v470H175c-4.4' +
|
||||
' 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h618c35.3 0 64-' +
|
||||
'28.7 64-64V306c0-35.3-28.7-64-64-64z'
|
||||
|
||||
const arrowPath = 'M765.7 486.8L314.9 134.7c-5.3-4.1' +
|
||||
'-12.9-0.4-12.9 6.3v77.3c0 4.9 2.3 9.6 6.1 12.6l36' +
|
||||
'0 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6' +
|
||||
'.7 7.7 10.4 12.9 6.3l450.8-352.1c16.4-12.8 16.4-3' +
|
||||
'7.6 0-50.4z'
|
||||
|
||||
const getSvg = (h, path, iStyle = {}, style = {}) => {
|
||||
return (
|
||||
<i style={iStyle}>
|
||||
<svg
|
||||
viewBox='0 0 1024 1024'
|
||||
width='1em'
|
||||
height='1em'
|
||||
fill='currentColor'
|
||||
style={{ verticalAlign: '-.125em', ...style }}
|
||||
>
|
||||
<path d={path} />
|
||||
</svg>
|
||||
</i>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
data () {
|
||||
const h = this.$createElement
|
||||
const switcherIcon = (obj) => {
|
||||
if (obj.isLeaf) {
|
||||
return getSvg(h, arrowPath,
|
||||
{ cursor: 'pointer', backgroundColor: 'white' },
|
||||
{ transform: 'rotate(270deg)' })
|
||||
}
|
||||
return getSvg(h, arrowPath,
|
||||
{ cursor: 'pointer', backgroundColor: 'white' },
|
||||
{ transform: `rotate(${obj.expanded ? 90 : 0}deg)` })
|
||||
}
|
||||
|
||||
const inputIcon = getSvg(h, bubblePath)
|
||||
const clearIcon = getSvg(h, clearPath)
|
||||
const removeIcon = getSvg(h, clearPath)
|
||||
return {
|
||||
iconProps: {
|
||||
inputIcon,
|
||||
clearIcon,
|
||||
removeIcon,
|
||||
switcherIcon,
|
||||
},
|
||||
|
||||
iconPropsFunction: {
|
||||
inputIcon: () => inputIcon,
|
||||
clearIcon: () => clearIcon,
|
||||
removeIcon: () => removeIcon,
|
||||
switcherIcon,
|
||||
},
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div class='custom-icon-demo'>
|
||||
<h2>Single</h2>
|
||||
<TreeSelect
|
||||
treeData={gData}
|
||||
placeholder={<span>Please Select</span>}
|
||||
transitionName='rc-tree-select-dropdown-slide-up'
|
||||
style={{ width: '300px' }}
|
||||
dropdownStyle={{ maxHeight: '200px', overflow: 'auto', zIndex: 1500 }}
|
||||
showSearch allowClear
|
||||
{...{ props: { ...this.iconProps }}}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
<br />
|
||||
<h2>Multiple</h2>
|
||||
<TreeSelect
|
||||
treeData={gData}
|
||||
multiple
|
||||
placeholder={<span>Please Select</span>}
|
||||
transitionName='rc-tree-select-dropdown-slide-up'
|
||||
style={{ width: '300px' }}
|
||||
dropdownStyle={{ maxHeight: '200px', overflow: 'auto', zIndex: 1500 }}
|
||||
showSearch allowClear
|
||||
{...{ props: { ...this.iconPropsFunction }}}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
.rc-tree-select-selection--multiple {
|
||||
max-height: 50px;
|
||||
overflow-y: scroll;
|
||||
@ -12,4 +11,4 @@
|
||||
.rc-tree-select-selection--multiple {
|
||||
min-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
/* eslint react/no-multi-comp:0, no-console:0 */
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import '../assets/index.less'
|
||||
import TreeSelect from '../index'
|
||||
import TreeSelect from '../src/index'
|
||||
|
||||
const SHOW_PARENT = TreeSelect.SHOW_PARENT
|
||||
|
||||
@ -33,20 +34,18 @@ const treeData = [{
|
||||
}]
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
value: ['0-0-0'],
|
||||
disabled: false,
|
||||
}
|
||||
},
|
||||
|
||||
mixins: [BaseMixin],
|
||||
data: () => ({
|
||||
value: ['0-0-0'],
|
||||
disabled: false,
|
||||
}),
|
||||
methods: {
|
||||
onChange (value) {
|
||||
onChange (value) {
|
||||
console.log('onChange ', value, arguments)
|
||||
this.value = value
|
||||
this.setState({ value })
|
||||
},
|
||||
switch (checked) {
|
||||
this.disabled = checked
|
||||
this.setState({ disabled: checked })
|
||||
},
|
||||
},
|
||||
|
||||
@ -61,6 +60,7 @@ export default {
|
||||
treeCheckable: true,
|
||||
showCheckedStrategy: SHOW_PARENT,
|
||||
searchPlaceholder: 'Please select',
|
||||
__propsSymbol__: Symbol(),
|
||||
},
|
||||
on: {
|
||||
change: this.onChange,
|
||||
@ -77,4 +77,3 @@ export default {
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -1,35 +1,36 @@
|
||||
/* eslint react/no-multi-comp:0, no-console:0 */
|
||||
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import '../assets/index.less'
|
||||
import TreeSelect from '../index'
|
||||
import TreeSelect from '../src/index'
|
||||
import { getNewTreeData, generateTreeNodes } from './util'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
treeData: [
|
||||
{ label: 'pNode 01', value: '0-0', key: '0-0' },
|
||||
{ label: 'pNode 02', value: '0-1', key: '0-1' },
|
||||
{ label: 'pNode 03', value: '0-2', key: '0-2', isLeaf: true },
|
||||
],
|
||||
// value: '0-0',
|
||||
value: { value: '0-0-0-value', label: '0-0-0-label' },
|
||||
}
|
||||
},
|
||||
|
||||
mixins: [BaseMixin],
|
||||
data: () => ({
|
||||
treeData: [
|
||||
{ label: 'pNode 01', value: '0-0', key: '0-0' },
|
||||
{ label: 'pNode 02', value: '0-1', key: '0-1' },
|
||||
{ label: 'pNode 03', value: '0-2', key: '0-2', isLeaf: true },
|
||||
],
|
||||
// value: '0-0',
|
||||
value: { value: '0-0-0-value', label: '0-0-0-label' },
|
||||
}),
|
||||
methods: {
|
||||
onChange (value) {
|
||||
onChange (value) {
|
||||
console.log(value)
|
||||
this.value = value
|
||||
this.setState({
|
||||
value,
|
||||
})
|
||||
},
|
||||
|
||||
onLoadData (treeNode) {
|
||||
onLoadData (treeNode) {
|
||||
console.log(treeNode)
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const treeData = [...this.treeData]
|
||||
getNewTreeData(treeData, treeNode.eventKey, generateTreeNodes(treeNode), 2)
|
||||
this.treeData = treeData
|
||||
this.setState({ treeData })
|
||||
resolve()
|
||||
}, 500)
|
||||
})
|
||||
@ -47,9 +48,9 @@ export default {
|
||||
value={this.value}
|
||||
onChange={this.onChange}
|
||||
loadData={this.onLoadData}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ export default {
|
||||
treeCheckable
|
||||
onChange={this.onChange}
|
||||
onSelect={this.onSelect}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
|
||||
<h2>use treeDataSimpleMode</h2>
|
||||
@ -89,6 +90,7 @@ export default {
|
||||
treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||
onChange={this.onChange}
|
||||
onSelect={this.onSelect}
|
||||
__propsSymbol__={Symbol()}
|
||||
/>
|
||||
<button onClick={this.onDataChange}>change data</button>
|
||||
</div>
|
@ -25,7 +25,7 @@ const TreeSelectInput = {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<TreeSelect {...{ props: this.$props }} onChange={this.onChange.bind(this)} />
|
||||
<TreeSelect {...{ props: this.$props }} onChange={this.onChange} />
|
||||
)
|
||||
},
|
||||
}
|
||||
@ -57,6 +57,7 @@ const Form = {
|
||||
multiple: true,
|
||||
treeData: gData,
|
||||
treeCheckable: true,
|
||||
__propsSymbol__: Symbol(),
|
||||
// treeDefaultExpandAll: true,
|
||||
},
|
||||
}
|
||||
|
@ -27,13 +27,15 @@ export function generateData (x = 3, y = 2, z = 1, gData = []) {
|
||||
tns[index].children = []
|
||||
return _loop(__level, key, tns[index].children)
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
_loop(z)
|
||||
return gData
|
||||
}
|
||||
export function calcTotal (x = 3, y = 2, z = 1) {
|
||||
/* eslint no-param-reassign:0*/
|
||||
const rec = (n) => n >= 0 ? x * Math.pow(y, n--) + rec(n) : 0
|
||||
/* eslint no-param-reassign:0 */
|
||||
const rec = (n) => n >= 0 ? x * (y ** (n--)) + rec(n) : 0
|
||||
return rec(z + 1)
|
||||
}
|
||||
console.log('总节点数(单个tree):', calcTotal())
|
||||
|
@ -1,7 +1,10 @@
|
||||
// rc-tree-select 1.12.13 tag
|
||||
// export this package's api
|
||||
// base 2.4.4
|
||||
import Vue from 'vue'
|
||||
import TreeSelect from './src'
|
||||
import ref from 'vue-ref'
|
||||
|
||||
Vue.use(ref, { name: 'ant-ref' })
|
||||
export default TreeSelect
|
||||
|
||||
export { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './src'
|
||||
|
284
components/vc-tree-select/src/Base/BasePopup.jsx
Normal file
284
components/vc-tree-select/src/Base/BasePopup.jsx
Normal file
@ -0,0 +1,284 @@
|
||||
import warning from 'warning'
|
||||
import PropTypes from '../../../_util/vue-types'
|
||||
import { Tree } from '../../../vc-tree'
|
||||
import BaseMixin from '../../../_util/BaseMixin'
|
||||
|
||||
// export const popupContextTypes = {
|
||||
// onPopupKeyDown: PropTypes.func.isRequired,
|
||||
// onTreeNodeSelect: PropTypes.func.isRequired,
|
||||
// onTreeNodeCheck: PropTypes.func.isRequired,
|
||||
// }
|
||||
function getDerivedStateFromProps (nextProps, prevState) {
|
||||
const { _prevProps: prevProps = {},
|
||||
_loadedKeys: loadedKeys,
|
||||
_expandedKeyList: expandedKeyList,
|
||||
_cachedExpandedKeyList: cachedExpandedKeyList,
|
||||
} = prevState || {}
|
||||
const {
|
||||
valueList, valueEntities, keyEntities,
|
||||
treeExpandedKeys, filteredTreeNodes, searchValue,
|
||||
} = nextProps
|
||||
|
||||
const newState = {
|
||||
_prevProps: { ...nextProps },
|
||||
}
|
||||
|
||||
// Check value update
|
||||
if (valueList !== prevProps.valueList) {
|
||||
newState._keyList = valueList
|
||||
.map(({ value }) => valueEntities[value])
|
||||
.filter(entity => entity)
|
||||
.map(({ key }) => key)
|
||||
}
|
||||
|
||||
// Show all when tree is in filter mode
|
||||
if (
|
||||
!treeExpandedKeys &&
|
||||
filteredTreeNodes &&
|
||||
filteredTreeNodes.length &&
|
||||
filteredTreeNodes !== prevProps.filteredTreeNodes
|
||||
) {
|
||||
newState._expandedKeyList = Object.keys(keyEntities)
|
||||
}
|
||||
|
||||
// Cache `expandedKeyList` when filter set
|
||||
if (searchValue && !prevProps.searchValue) {
|
||||
newState._cachedExpandedKeyList = expandedKeyList
|
||||
} else if (!searchValue && prevProps.searchValue && !treeExpandedKeys) {
|
||||
newState._expandedKeyList = cachedExpandedKeyList || []
|
||||
newState._cachedExpandedKeyList = []
|
||||
}
|
||||
|
||||
// Use expandedKeys if provided
|
||||
if (prevProps.treeExpandedKeys !== treeExpandedKeys) {
|
||||
newState._expandedKeyList = treeExpandedKeys
|
||||
}
|
||||
|
||||
// Clean loadedKeys if key not exist in keyEntities anymore
|
||||
if (nextProps.loadData) {
|
||||
newState._loadedKeys = loadedKeys.filter(key => key in keyEntities)
|
||||
}
|
||||
|
||||
return newState
|
||||
}
|
||||
const BasePopup = {
|
||||
mixins: [BaseMixin],
|
||||
name: 'BasePopup',
|
||||
props: {
|
||||
prefixCls: PropTypes.string,
|
||||
upperSearchValue: PropTypes.string,
|
||||
valueList: PropTypes.array,
|
||||
searchHalfCheckedKeys: PropTypes.array,
|
||||
valueEntities: PropTypes.object,
|
||||
keyEntities: PropTypes.object,
|
||||
treeIcon: PropTypes.bool,
|
||||
treeLine: PropTypes.bool,
|
||||
treeNodeFilterProp: PropTypes.string,
|
||||
treeCheckable: PropTypes.any,
|
||||
treeCheckStrictly: PropTypes.bool,
|
||||
treeDefaultExpandAll: PropTypes.bool,
|
||||
treeDefaultExpandedKeys: PropTypes.array,
|
||||
treeExpandedKeys: PropTypes.array,
|
||||
loadData: PropTypes.func,
|
||||
multiple: PropTypes.bool,
|
||||
// onTreeExpand: PropTypes.func,
|
||||
searchValue: PropTypes.string,
|
||||
treeNodes: PropTypes.any,
|
||||
filteredTreeNodes: PropTypes.any,
|
||||
notFoundContent: PropTypes.string,
|
||||
|
||||
ariaId: PropTypes.string,
|
||||
switcherIcon: PropTypes.any,
|
||||
// HOC
|
||||
renderSearch: PropTypes.func,
|
||||
// onTreeExpanded: PropTypes.func,
|
||||
|
||||
__propsSymbol__: PropTypes.any,
|
||||
},
|
||||
inject: {
|
||||
vcTreeSelect: { default: {}},
|
||||
},
|
||||
watch: {
|
||||
__propsSymbol__ () {
|
||||
const state = getDerivedStateFromProps(this.$props, this.$data)
|
||||
this.setState(state)
|
||||
},
|
||||
},
|
||||
data () {
|
||||
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__')
|
||||
const {
|
||||
treeDefaultExpandAll, treeDefaultExpandedKeys,
|
||||
keyEntities,
|
||||
} = this.$props
|
||||
|
||||
// TODO: make `expandedKeyList` control
|
||||
let expandedKeyList = treeDefaultExpandedKeys
|
||||
if (treeDefaultExpandAll) {
|
||||
expandedKeyList = Object.keys(keyEntities)
|
||||
}
|
||||
|
||||
const state = {
|
||||
_keyList: [],
|
||||
_expandedKeyList: expandedKeyList,
|
||||
// Cache `expandedKeyList` when tree is in filter. This is used in `getDerivedStateFromProps`
|
||||
_cachedExpandedKeyList: [], // eslint-disable-line react/no-unused-state
|
||||
_loadedKeys: [],
|
||||
_prevProps: {},
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
...getDerivedStateFromProps(this.$props, state),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onTreeExpand (expandedKeyList) {
|
||||
const { treeExpandedKeys } = this.$props
|
||||
|
||||
// Set uncontrolled state
|
||||
if (!treeExpandedKeys) {
|
||||
this.setState({ _expandedKeyList: expandedKeyList }, () => {
|
||||
this.__emit('treeExpanded')
|
||||
})
|
||||
}
|
||||
this.__emit('treeExpand', expandedKeyList)
|
||||
},
|
||||
|
||||
onLoad (loadedKeys) {
|
||||
this.setState({ _loadedKeys: loadedKeys })
|
||||
},
|
||||
|
||||
/**
|
||||
* Not pass `loadData` when searching. To avoid loop ajax call makes browser crash.
|
||||
*/
|
||||
getLoadData () {
|
||||
const { loadData, searchValue } = this.$props
|
||||
if (searchValue) return null
|
||||
return loadData
|
||||
},
|
||||
|
||||
/**
|
||||
* This method pass to Tree component which is used for add filtered class
|
||||
* in TreeNode > li
|
||||
*/
|
||||
filterTreeNode (treeNode) {
|
||||
const { upperSearchValue, treeNodeFilterProp } = this.$props
|
||||
|
||||
const filterVal = treeNode[treeNodeFilterProp]
|
||||
if (typeof filterVal === 'string') {
|
||||
return upperSearchValue && (filterVal).toUpperCase().indexOf(upperSearchValue) !== -1
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
renderNotFound () {
|
||||
const { prefixCls, notFoundContent } = this.$props
|
||||
|
||||
return (
|
||||
<span class={`${prefixCls}-not-found`}>
|
||||
{notFoundContent}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { _keyList: keyList, _expandedKeyList: expandedKeyList, _loadedKeys: loadedKeys } = this.$data
|
||||
const {
|
||||
prefixCls,
|
||||
treeNodes, filteredTreeNodes,
|
||||
treeIcon, treeLine, treeCheckable, treeCheckStrictly, multiple,
|
||||
ariaId,
|
||||
renderSearch,
|
||||
switcherIcon,
|
||||
searchHalfCheckedKeys,
|
||||
} = this.$props
|
||||
const { vcTreeSelect: {
|
||||
onPopupKeyDown,
|
||||
onTreeNodeSelect,
|
||||
onTreeNodeCheck,
|
||||
}} = this
|
||||
|
||||
const loadData = this.getLoadData()
|
||||
|
||||
const treeProps = {}
|
||||
|
||||
if (treeCheckable) {
|
||||
treeProps.checkedKeys = keyList
|
||||
} else {
|
||||
treeProps.selectedKeys = keyList
|
||||
}
|
||||
let $notFound
|
||||
let $treeNodes
|
||||
if (filteredTreeNodes) {
|
||||
if (filteredTreeNodes.length) {
|
||||
treeProps.checkStrictly = true
|
||||
$treeNodes = filteredTreeNodes
|
||||
|
||||
// Fill halfCheckedKeys
|
||||
if (treeCheckable && !treeCheckStrictly) {
|
||||
treeProps.checkedKeys = {
|
||||
checked: keyList,
|
||||
halfChecked: searchHalfCheckedKeys,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$notFound = this.renderNotFound()
|
||||
}
|
||||
} else if (!treeNodes.length) {
|
||||
$notFound = this.renderNotFound()
|
||||
} else {
|
||||
$treeNodes = treeNodes
|
||||
}
|
||||
|
||||
let $tree
|
||||
if ($notFound) {
|
||||
$tree = $notFound
|
||||
} else {
|
||||
const treeAllProps = {
|
||||
props: {
|
||||
prefixCls: `${prefixCls}-tree`,
|
||||
showIcon: treeIcon,
|
||||
showLine: treeLine,
|
||||
selectable: !treeCheckable,
|
||||
checkable: treeCheckable,
|
||||
checkStrictly: treeCheckStrictly,
|
||||
multiple: multiple,
|
||||
loadData: loadData,
|
||||
loadedKeys: loadedKeys,
|
||||
expandedKeys: expandedKeyList,
|
||||
filterTreeNode: this.filterTreeNode,
|
||||
switcherIcon: switcherIcon,
|
||||
...treeProps,
|
||||
__propsSymbol__: Symbol(),
|
||||
children: $treeNodes,
|
||||
},
|
||||
on: {
|
||||
select: onTreeNodeSelect,
|
||||
check: onTreeNodeCheck,
|
||||
expand: this.onTreeExpand,
|
||||
load: this.onLoad,
|
||||
},
|
||||
}
|
||||
$tree = (
|
||||
<Tree
|
||||
{...treeAllProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role='listbox'
|
||||
id={ariaId}
|
||||
onKeydown={onPopupKeyDown}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{renderSearch ? renderSearch() : null}
|
||||
{$tree}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default BasePopup
|
187
components/vc-tree-select/src/Base/BaseSelector.jsx
Normal file
187
components/vc-tree-select/src/Base/BaseSelector.jsx
Normal file
@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Input Box is in different position for different mode.
|
||||
* This not the same design as `Select` cause it's followed by antd 0.x `Select`.
|
||||
* We will not follow the new design immediately since antd 3.x is already released.
|
||||
*
|
||||
* So this file named as Selector to avoid confuse.
|
||||
*/
|
||||
import { createRef } from '../util'
|
||||
import PropTypes from '../../../_util/vue-types'
|
||||
import classNames from 'classnames'
|
||||
import { initDefaultProps, getComponentFromProp } from '../../../_util/props-util'
|
||||
import BaseMixin from '../../../_util/BaseMixin'
|
||||
export const selectorPropTypes = () => ({
|
||||
prefixCls: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
open: PropTypes.bool,
|
||||
valueList: PropTypes.array, // Name as valueList to diff the single value
|
||||
allowClear: PropTypes.bool,
|
||||
showArrow: PropTypes.bool,
|
||||
// onClick: PropTypes.func,
|
||||
// onBlur: PropTypes.func,
|
||||
// onFocus: PropTypes.func,
|
||||
removeSelected: PropTypes.func,
|
||||
choiceTransitionName: PropTypes.string,
|
||||
// Pass by component
|
||||
ariaId: PropTypes.string,
|
||||
inputIcon: PropTypes.any,
|
||||
clearIcon: PropTypes.any,
|
||||
removeIcon: PropTypes.any,
|
||||
selectorValueList: PropTypes.array,
|
||||
placeholder: PropTypes.any,
|
||||
disabled: PropTypes.bool,
|
||||
focused: PropTypes.bool,
|
||||
})
|
||||
|
||||
function noop () {}
|
||||
export default function (modeName) {
|
||||
const BaseSelector = {
|
||||
name: 'BaseSelector',
|
||||
mixins: [BaseMixin],
|
||||
props: initDefaultProps({
|
||||
...selectorPropTypes(),
|
||||
|
||||
// Pass by HOC
|
||||
renderSelection: PropTypes.func.isRequired,
|
||||
renderPlaceholder: PropTypes.func,
|
||||
tabIndex: PropTypes.number,
|
||||
}, {
|
||||
tabIndex: 0,
|
||||
}),
|
||||
inject: {
|
||||
vcTreeSelect: { default: {}},
|
||||
},
|
||||
created () {
|
||||
this.domRef = createRef()
|
||||
},
|
||||
methods: {
|
||||
onFocus (e) {
|
||||
const { focused } = this.$props
|
||||
const { vcTreeSelect: { onSelectorFocus }} = this
|
||||
|
||||
if (!focused) {
|
||||
onSelectorFocus()
|
||||
}
|
||||
this.__emit('focus', e)
|
||||
},
|
||||
|
||||
onBlur (e) {
|
||||
const { vcTreeSelect: { onSelectorBlur }} = this
|
||||
|
||||
// TODO: Not trigger when is inner component get focused
|
||||
onSelectorBlur()
|
||||
this.__emit('blur', e)
|
||||
},
|
||||
|
||||
focus () {
|
||||
this.domRef.current.focus()
|
||||
},
|
||||
|
||||
blur () {
|
||||
this.domRef.current.blur()
|
||||
},
|
||||
|
||||
renderClear () {
|
||||
const { prefixCls, allowClear, valueList } = this.$props
|
||||
const { vcTreeSelect: { onSelectorClear }} = this
|
||||
|
||||
if (!allowClear || !valueList.length || !valueList[0].value) {
|
||||
return null
|
||||
}
|
||||
const clearIcon = getComponentFromProp(this, 'clearIcon')
|
||||
return (
|
||||
<span
|
||||
key='clear'
|
||||
class={`${prefixCls}-selection__clear`}
|
||||
onClick={onSelectorClear}
|
||||
>
|
||||
{clearIcon}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
|
||||
renderArrow () {
|
||||
const { prefixCls, showArrow } = this.$props
|
||||
if (!showArrow) {
|
||||
return null
|
||||
}
|
||||
const inputIcon = getComponentFromProp(this, 'inputIcon')
|
||||
return (
|
||||
<span
|
||||
key='arrow'
|
||||
class={`${prefixCls}-arrow`}
|
||||
style={{ outline: 'none' }}
|
||||
>
|
||||
{inputIcon}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const {
|
||||
prefixCls, className, style,
|
||||
open, focused, disabled, allowClear,
|
||||
ariaId,
|
||||
renderSelection, renderPlaceholder,
|
||||
tabIndex,
|
||||
} = this.$props
|
||||
const { vcTreeSelect: { onSelectorKeyDown }, $listeners } = this
|
||||
|
||||
let myTabIndex = tabIndex
|
||||
if (disabled) {
|
||||
myTabIndex = null
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
style={style}
|
||||
onClick={$listeners.click || noop}
|
||||
class={classNames(
|
||||
className,
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-open`]: open,
|
||||
[`${prefixCls}-focused`]: open || focused,
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
[`${prefixCls}-enabled`]: !disabled,
|
||||
[`${prefixCls}-allow-clear`]: allowClear,
|
||||
}
|
||||
)}
|
||||
{...{
|
||||
directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.domRef,
|
||||
}],
|
||||
}}
|
||||
role='combobox'
|
||||
aria-expanded={open}
|
||||
aria-owns={open ? ariaId : undefined}
|
||||
aria-controls={open ? ariaId : undefined}
|
||||
aria-haspopup='listbox'
|
||||
aria-disabled={disabled}
|
||||
tabIndex={myTabIndex}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onKeydown={onSelectorKeyDown}
|
||||
>
|
||||
<span
|
||||
key='selection'
|
||||
class={classNames(
|
||||
`${prefixCls}-selection`,
|
||||
`${prefixCls}-selection--${modeName}`
|
||||
)}
|
||||
>
|
||||
{renderSelection()}
|
||||
{this.renderClear()}
|
||||
{this.renderArrow()}
|
||||
|
||||
{renderPlaceholder && renderPlaceholder()}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
return BaseSelector
|
||||
}
|
3
components/vc-tree-select/src/Popup/MultiplePopup.jsx
Normal file
3
components/vc-tree-select/src/Popup/MultiplePopup.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
import BasePopup from '../Base/BasePopup'
|
||||
|
||||
export default BasePopup
|
80
components/vc-tree-select/src/Popup/SinglePopup.jsx
Normal file
80
components/vc-tree-select/src/Popup/SinglePopup.jsx
Normal file
@ -0,0 +1,80 @@
|
||||
import PropTypes from '../../../_util/vue-types'
|
||||
import BasePopup from '../Base/BasePopup'
|
||||
import SearchInput from '../SearchInput'
|
||||
import { createRef } from '../util'
|
||||
|
||||
const SinglePopup = {
|
||||
name: 'SinglePopup',
|
||||
props: {
|
||||
...BasePopup.props,
|
||||
...SearchInput.props,
|
||||
searchValue: PropTypes.string,
|
||||
showSearch: PropTypes.bool,
|
||||
dropdownPrefixCls: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
},
|
||||
created () {
|
||||
this.inputRef = createRef()
|
||||
},
|
||||
methods: {
|
||||
onPlaceholderClick () {
|
||||
this.inputRef.current.focus()
|
||||
},
|
||||
|
||||
_renderPlaceholder () {
|
||||
const { searchPlaceholder, searchValue, prefixCls } = this.$props
|
||||
|
||||
if (!searchPlaceholder) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
display: searchValue ? 'none' : 'block',
|
||||
}}
|
||||
onClick={this.onPlaceholderClick}
|
||||
class={`${prefixCls}-search__field__placeholder`}
|
||||
>
|
||||
{searchPlaceholder}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
|
||||
_renderSearch () {
|
||||
const { showSearch, dropdownPrefixCls } = this.$props
|
||||
|
||||
if (!showSearch) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<span class={`${dropdownPrefixCls}-search`}>
|
||||
<SearchInput
|
||||
{...{
|
||||
props: { ...this.$props, renderPlaceholder: this._renderPlaceholder },
|
||||
on: this.$listeners,
|
||||
directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.inputRef,
|
||||
}],
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<BasePopup
|
||||
{...{
|
||||
props: { ...this.$props, renderSearch: this._renderSearch, __propsSymbol__: Symbol() },
|
||||
on: this.$listeners,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default SinglePopup
|
@ -1,62 +1,46 @@
|
||||
import PropTypes from '../../_util/vue-types'
|
||||
import { SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './strategies'
|
||||
import { isLabelInValue } from './util'
|
||||
|
||||
export const SelectPropTypes = {
|
||||
// className: PropTypes.string,
|
||||
prefixCls: PropTypes.string,
|
||||
multiple: PropTypes.bool,
|
||||
filterTreeNode: PropTypes.any,
|
||||
showSearch: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
showArrow: PropTypes.bool,
|
||||
allowClear: PropTypes.bool,
|
||||
defaultOpen: PropTypes.bool,
|
||||
open: PropTypes.bool,
|
||||
transitionName: PropTypes.string,
|
||||
animation: PropTypes.string,
|
||||
choiceTransitionName: PropTypes.string,
|
||||
// onClick: PropTypes.func,
|
||||
// onChange: PropTypes.func,
|
||||
// onSelect: PropTypes.func,
|
||||
// onDeselect: PropTypes.func,
|
||||
// onSearch: PropTypes.func,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
placeholder: PropTypes.any,
|
||||
inputValue: PropTypes.any,
|
||||
value: PropTypes.any,
|
||||
defaultValue: PropTypes.any,
|
||||
label: PropTypes.any, // vnode
|
||||
defaultLabel: PropTypes.any,
|
||||
labelInValue: PropTypes.bool,
|
||||
dropdownClassName: PropTypes.string,
|
||||
dropdownStyle: PropTypes.object,
|
||||
dropdownPopupAlign: PropTypes.object,
|
||||
dropdownVisibleChange: PropTypes.func,
|
||||
maxTagTextLength: PropTypes.number,
|
||||
showCheckedStrategy: PropTypes.oneOf([
|
||||
SHOW_ALL, SHOW_PARENT, SHOW_CHILD,
|
||||
]),
|
||||
treeCheckStrictly: PropTypes.bool,
|
||||
treeIcon: PropTypes.bool,
|
||||
treeLine: PropTypes.bool,
|
||||
treeDefaultExpandAll: PropTypes.bool,
|
||||
treeDefaultExpandedKeys: PropTypes.arrayOf(String),
|
||||
treeCheckable: PropTypes.any, // bool vnode
|
||||
treeNodeLabelProp: PropTypes.string,
|
||||
treeNodeFilterProp: PropTypes.string,
|
||||
treeData: PropTypes.array,
|
||||
treeDataSimpleMode: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.object,
|
||||
]),
|
||||
loadData: PropTypes.func,
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
notFoundContent: PropTypes.any,
|
||||
children: PropTypes.any,
|
||||
autoFocus: PropTypes.bool,
|
||||
getPopupContainer: PropTypes.func,
|
||||
switcherIcon: PropTypes.func,
|
||||
inputIcon: PropTypes.any,
|
||||
removeIcon: PropTypes.any,
|
||||
clearIcon: PropTypes.any,
|
||||
const internalValProp = PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
])
|
||||
|
||||
export function genArrProps (propType) {
|
||||
return PropTypes.oneOfType([
|
||||
propType,
|
||||
PropTypes.arrayOf(propType),
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin code check `multiple` is true when `treeCheckStrictly` & `labelInValue`.
|
||||
* But in process logic is already cover to array.
|
||||
* Check array is not necessary. Let's simplify this check logic.
|
||||
*/
|
||||
export function valueProp (...args) {
|
||||
const [props, propName, Component] = args
|
||||
|
||||
if (isLabelInValue(props)) {
|
||||
const err = genArrProps(PropTypes.shape({
|
||||
label: PropTypes.node,
|
||||
value: internalValProp,
|
||||
}).loose)(...args)
|
||||
if (err) {
|
||||
return new Error(
|
||||
`Invalid prop \`${propName}\` supplied to \`${Component}\`. ` +
|
||||
`You should use { label: string, value: string | number } or [{ label: string, value: string | number }] instead.`
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const err = genArrProps(internalValProp)(...args)
|
||||
if (err) {
|
||||
return new Error(
|
||||
`Invalid prop \`${propName}\` supplied to \`${Component}\`. ` +
|
||||
`You should use string or [string] instead.`
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
127
components/vc-tree-select/src/SearchInput.jsx
Normal file
127
components/vc-tree-select/src/SearchInput.jsx
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Since search box is in different position with different mode.
|
||||
* - Single: in the popup box
|
||||
* - multiple: in the selector
|
||||
* Move the code as a SearchInput for easy management.
|
||||
*/
|
||||
|
||||
import PropTypes from '../../_util/vue-types'
|
||||
import { createRef } from './util'
|
||||
|
||||
const SearchInput = {
|
||||
name: 'SearchInput',
|
||||
props: {
|
||||
open: PropTypes.bool,
|
||||
searchValue: PropTypes.string,
|
||||
prefixCls: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
renderPlaceholder: PropTypes.func,
|
||||
needAlign: PropTypes.bool,
|
||||
ariaId: PropTypes.string,
|
||||
},
|
||||
inject: {
|
||||
vcTreeSelect: { default: {}},
|
||||
},
|
||||
|
||||
created () {
|
||||
this.inputRef = createRef()
|
||||
this.mirrorInputRef = createRef()
|
||||
this.prevProps = { ...this.$props }
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
const { open, needAlign } = this.$props
|
||||
if (needAlign) {
|
||||
this.alignInputWidth()
|
||||
}
|
||||
|
||||
if (open) {
|
||||
this.focus(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
updated () {
|
||||
const { open, searchValue, needAlign } = this.$props
|
||||
const { prevProps } = this
|
||||
this.$nextTick(() => {
|
||||
if (open && prevProps.open !== open) {
|
||||
this.focus()
|
||||
}
|
||||
if (needAlign && searchValue !== prevProps.searchValue) {
|
||||
this.alignInputWidth()
|
||||
}
|
||||
this.prevProps = { ...this.$props }
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* `scrollWidth` is not correct in IE, do the workaround.
|
||||
* ref: https://github.com/react-component/tree-select/issues/65
|
||||
* clientWidth 0 when mounted in vue. why?
|
||||
*/
|
||||
alignInputWidth () {
|
||||
this.inputRef.current.style.width =
|
||||
`${this.mirrorInputRef.current.clientWidth || this.mirrorInputRef.current.offsetWidth}px`
|
||||
},
|
||||
|
||||
/**
|
||||
* Need additional timeout for focus cause parent dom is not ready when didMount trigger
|
||||
*/
|
||||
focus (isDidMount) {
|
||||
if (this.inputRef.current) {
|
||||
this.inputRef.current.focus()
|
||||
if (isDidMount) {
|
||||
setTimeout(() => {
|
||||
this.inputRef.current.focus()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
blur () {
|
||||
if (this.inputRef.current) {
|
||||
this.inputRef.current.blur()
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { searchValue, prefixCls, disabled, renderPlaceholder, open, ariaId } = this.$props
|
||||
const { vcTreeSelect: {
|
||||
onSearchInputChange, onSearchInputKeyDown,
|
||||
}} = this
|
||||
return (
|
||||
<span class={`${prefixCls}-search__field__wrap`}>
|
||||
<input
|
||||
type='text'
|
||||
{...{ directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.inputRef,
|
||||
}] }}
|
||||
onInput={onSearchInputChange}
|
||||
onKeydown={onSearchInputKeyDown}
|
||||
value={searchValue}
|
||||
disabled={disabled}
|
||||
class={`${prefixCls}-search__field`}
|
||||
aria-label='filter select'
|
||||
aria-autocomplete='list'
|
||||
aria-controls={open ? ariaId : undefined}
|
||||
aria-multiline='false'
|
||||
/>
|
||||
<span
|
||||
{...{ directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.mirrorInputRef,
|
||||
}] }}
|
||||
class={`${prefixCls}-search__field__mirror`}
|
||||
>
|
||||
{searchValue}
|
||||
</span>
|
||||
{renderPlaceholder ? renderPlaceholder() : null}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default SearchInput
|
File diff suppressed because it is too large
Load Diff
28
components/vc-tree-select/src/SelectNode.jsx
Normal file
28
components/vc-tree-select/src/SelectNode.jsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { TreeNode } from '../../vc-tree'
|
||||
/**
|
||||
* SelectNode wrapped the tree node.
|
||||
* Let's use SelectNode instead of TreeNode
|
||||
* since TreeNode is so confuse here.
|
||||
*/
|
||||
export default {
|
||||
functional: true,
|
||||
name: 'SelectNode',
|
||||
isTreeNode: true,
|
||||
props: TreeNode.props,
|
||||
render (h, context) {
|
||||
const { props, slots, listeners, data } = context
|
||||
const $slots = slots()
|
||||
const children = $slots.default
|
||||
delete $slots.default
|
||||
const treeNodeProps = {
|
||||
...data, on: { ...listeners, ...data.nativeOn }, props,
|
||||
}
|
||||
const slotsKey = Object.keys($slots)
|
||||
return <TreeNode {...treeNodeProps}>
|
||||
{children}
|
||||
{slotsKey.length ? slotsKey.map(name => {
|
||||
return <template slot={name}>{$slots[name]}</template>
|
||||
}) : null}
|
||||
</TreeNode>
|
||||
},
|
||||
}
|
@ -1,18 +1,8 @@
|
||||
import PropTypes from '../../_util/vue-types'
|
||||
import classnames from 'classnames'
|
||||
import Trigger from '../../vc-trigger'
|
||||
import Tree, { TreeNode } from '../../vc-tree'
|
||||
import { SelectPropTypes } from './PropTypes'
|
||||
import BaseMixin from '../../_util/BaseMixin'
|
||||
import {
|
||||
loopAllChildren,
|
||||
flatToHierarchy,
|
||||
getValuePropValue,
|
||||
labelCompatible,
|
||||
} from './util'
|
||||
|
||||
import { cloneElement } from '../../_util/vnode'
|
||||
import { getSlotOptions, getKey, getAllProps, getComponentFromProp } from '../../_util/props-util'
|
||||
import Trigger from '../../vc-trigger'
|
||||
import { createRef } from './util'
|
||||
import classNames from 'classnames'
|
||||
|
||||
const BUILT_IN_PLACEMENTS = {
|
||||
bottomLeft: {
|
||||
@ -22,6 +12,7 @@ const BUILT_IN_PLACEMENTS = {
|
||||
adjustX: 0,
|
||||
adjustY: 1,
|
||||
},
|
||||
ignoreShake: true,
|
||||
},
|
||||
topLeft: {
|
||||
points: ['bl', 'tl'],
|
||||
@ -30,335 +21,94 @@ const BUILT_IN_PLACEMENTS = {
|
||||
adjustX: 0,
|
||||
adjustY: 1,
|
||||
},
|
||||
ignoreShake: true,
|
||||
},
|
||||
}
|
||||
|
||||
const SelectTrigger = {
|
||||
mixins: [BaseMixin],
|
||||
name: 'SelectTrigger',
|
||||
props: {
|
||||
...SelectPropTypes,
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
dropdownPopupAlign: PropTypes.object,
|
||||
visible: PropTypes.bool,
|
||||
filterTreeNode: PropTypes.any,
|
||||
treeNodes: PropTypes.any,
|
||||
inputValue: PropTypes.string,
|
||||
// Pass by outside user props
|
||||
disabled: PropTypes.bool,
|
||||
showSearch: PropTypes.bool,
|
||||
prefixCls: PropTypes.string,
|
||||
popupClassName: PropTypes.string,
|
||||
_cachetreeData: PropTypes.any,
|
||||
_treeNodesStates: PropTypes.any,
|
||||
halfCheckedValues: PropTypes.any,
|
||||
inputElement: PropTypes.any,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
sExpandedKeys: [],
|
||||
fireOnExpand: false,
|
||||
dropdownWidth: null,
|
||||
}
|
||||
},
|
||||
dropdownPopupAlign: PropTypes.object,
|
||||
dropdownClassName: PropTypes.string,
|
||||
dropdownStyle: PropTypes.object,
|
||||
transitionName: PropTypes.string,
|
||||
animation: PropTypes.string,
|
||||
getPopupContainer: PropTypes.func,
|
||||
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
this.setDropdownWidth()
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
inputValue (val) {
|
||||
// set autoExpandParent to true
|
||||
this.setState({
|
||||
sExpandedKeys: [],
|
||||
fireOnExpand: false,
|
||||
})
|
||||
},
|
||||
},
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
|
||||
updated () {
|
||||
this.$nextTick(() => {
|
||||
this.setDropdownWidth()
|
||||
})
|
||||
// Pass by Select
|
||||
isMultiple: PropTypes.bool,
|
||||
dropdownPrefixCls: PropTypes.string,
|
||||
dropdownVisibleChange: PropTypes.func,
|
||||
popupElement: PropTypes.node,
|
||||
open: PropTypes.bool,
|
||||
},
|
||||
created () {
|
||||
this.triggerRef = createRef()
|
||||
},
|
||||
methods: {
|
||||
onExpand (expandedKeys) {
|
||||
// rerender
|
||||
this.setState({
|
||||
sExpandedKeys: expandedKeys,
|
||||
fireOnExpand: true,
|
||||
}, () => {
|
||||
// Fix https://github.com/ant-design/ant-design/issues/5689
|
||||
if (this.$refs.trigger && this.$refs.trigger.forcePopupAlign) {
|
||||
this.$refs.trigger.forcePopupAlign()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
setDropdownWidth () {
|
||||
const width = this.$el.offsetWidth
|
||||
if (width !== this.dropdownWidth) {
|
||||
this.setState({ dropdownWidth: width })
|
||||
}
|
||||
},
|
||||
|
||||
getPopupEleRefs () {
|
||||
return this.$refs.popupEle
|
||||
},
|
||||
|
||||
getPopupDOMNode () {
|
||||
return this.$refs.trigger.getPopupDomNode()
|
||||
},
|
||||
|
||||
getDropdownTransitionName () {
|
||||
const props = this.$props
|
||||
let transitionName = props.transitionName
|
||||
if (!transitionName && props.animation) {
|
||||
transitionName = `${this.getDropdownPrefixCls()}-${props.animation}`
|
||||
const { transitionName, animation, dropdownPrefixCls } = this.$props
|
||||
if (!transitionName && animation) {
|
||||
return `${dropdownPrefixCls}-${animation}`
|
||||
}
|
||||
return transitionName
|
||||
},
|
||||
|
||||
getDropdownPrefixCls () {
|
||||
return `${this.prefixCls}-dropdown`
|
||||
},
|
||||
|
||||
highlightTreeNode (treeNode) {
|
||||
const props = this.$props
|
||||
const filterVal = treeNode.$props[labelCompatible(props.treeNodeFilterProp)]
|
||||
if (typeof filterVal === 'string') {
|
||||
return props.inputValue && filterVal.indexOf(props.inputValue) > -1
|
||||
forcePopupAlign () {
|
||||
const $trigger = this.triggerRef.current
|
||||
if ($trigger) {
|
||||
$trigger.forcePopupAlign()
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
filterTreeNode_ (input, child) {
|
||||
if (!input) {
|
||||
return true
|
||||
}
|
||||
const filterTreeNode = this.filterTreeNode
|
||||
if (!filterTreeNode) {
|
||||
return true
|
||||
}
|
||||
const props = getAllProps(child)
|
||||
if (props && props.disabled) {
|
||||
return false
|
||||
}
|
||||
return filterTreeNode.call(this, input, child)
|
||||
},
|
||||
|
||||
processTreeNode (treeNodes) {
|
||||
const filterPoss = []
|
||||
this._expandedKeys = []
|
||||
loopAllChildren(treeNodes, (child, index, pos) => {
|
||||
if (this.filterTreeNode_(this.inputValue, child)) {
|
||||
filterPoss.push(pos)
|
||||
this._expandedKeys.push(String(getKey(child)))
|
||||
}
|
||||
})
|
||||
|
||||
// Include the filtered nodes's ancestral nodes.
|
||||
const processedPoss = []
|
||||
filterPoss.forEach(pos => {
|
||||
const arr = pos.split('-')
|
||||
arr.reduce((pre, cur) => {
|
||||
const res = `${pre}-${cur}`
|
||||
if (processedPoss.indexOf(res) < 0) {
|
||||
processedPoss.push(res)
|
||||
}
|
||||
return res
|
||||
})
|
||||
})
|
||||
const filterNodesPositions = []
|
||||
loopAllChildren(treeNodes, (child, index, pos) => {
|
||||
if (processedPoss.indexOf(pos) > -1) {
|
||||
filterNodesPositions.push({ node: child, pos })
|
||||
}
|
||||
})
|
||||
|
||||
const hierarchyNodes = flatToHierarchy(filterNodesPositions)
|
||||
|
||||
const recursive = children => {
|
||||
return children.map(child => {
|
||||
if (child.children) {
|
||||
return cloneElement(child.node, {
|
||||
children: recursive(child.children),
|
||||
})
|
||||
}
|
||||
return child.node
|
||||
})
|
||||
}
|
||||
return recursive(hierarchyNodes)
|
||||
},
|
||||
onSelect () {
|
||||
this.__emit('select', ...arguments)
|
||||
},
|
||||
|
||||
renderTree (keys, halfCheckedKeys, newTreeNodes, multiple) {
|
||||
const props = this.$props
|
||||
|
||||
const trProps = {
|
||||
multiple,
|
||||
prefixCls: `${props.prefixCls}-tree`,
|
||||
showIcon: props.treeIcon,
|
||||
showLine: props.treeLine,
|
||||
defaultExpandAll: props.treeDefaultExpandAll,
|
||||
defaultExpandedKeys: props.treeDefaultExpandedKeys,
|
||||
filterTreeNode: this.highlightTreeNode,
|
||||
}
|
||||
const trListeners = {}
|
||||
|
||||
if (props.treeCheckable) {
|
||||
trProps.selectable = false
|
||||
trProps.checkable = props.treeCheckable
|
||||
trListeners.check = this.onSelect
|
||||
trProps.checkStrictly = props.treeCheckStrictly
|
||||
if (props.inputValue) {
|
||||
// enable checkStrictly when search tree.
|
||||
trProps.checkStrictly = true
|
||||
} else {
|
||||
trProps._treeNodesStates = props._treeNodesStates
|
||||
}
|
||||
if (trProps.treeCheckStrictly && halfCheckedKeys.length) {
|
||||
trProps.checkedKeys = { checked: keys, halfChecked: halfCheckedKeys }
|
||||
} else {
|
||||
trProps.checkedKeys = keys
|
||||
}
|
||||
} else {
|
||||
trProps.selectedKeys = keys
|
||||
trListeners.select = this.onSelect
|
||||
}
|
||||
|
||||
// expand keys
|
||||
if (!trProps.defaultExpandAll && !trProps.defaultExpandedKeys && !props.loadData) {
|
||||
trProps.expandedKeys = keys
|
||||
}
|
||||
trProps.autoExpandParent = true
|
||||
trListeners.expand = this.onExpand
|
||||
if (this._expandedKeys && this._expandedKeys.length) {
|
||||
trProps.expandedKeys = this._expandedKeys
|
||||
}
|
||||
if (this.fireOnExpand) {
|
||||
trProps.expandedKeys = this.sExpandedKeys
|
||||
trProps.autoExpandParent = false
|
||||
}
|
||||
|
||||
// async loadData
|
||||
if (props.loadData) {
|
||||
trProps.loadData = props.loadData
|
||||
}
|
||||
return (
|
||||
<Tree ref='popupEle' {...{ props: trProps, on: trListeners }}>
|
||||
{newTreeNodes}
|
||||
</Tree>
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const props = this.$props
|
||||
const multiple = props.multiple
|
||||
const dropdownPrefixCls = this.getDropdownPrefixCls()
|
||||
const popupClassName = {
|
||||
[props.dropdownClassName]: !!props.dropdownClassName,
|
||||
[`${dropdownPrefixCls}--${multiple ? 'multiple' : 'single'}`]: 1,
|
||||
}
|
||||
let visible = props.visible
|
||||
const search = multiple || !props.showSearch ? null : (
|
||||
<span class={`${dropdownPrefixCls}-search`}>{props.inputElement}</span>
|
||||
)
|
||||
const recursive = children => {
|
||||
return children.map(function handler(child) { // eslint-disable-line
|
||||
// if (isEmptyElement(child) || (child.data && child.data.slot)) {
|
||||
// return null
|
||||
// }
|
||||
if (!getSlotOptions(child).__ANT_TREE_SELECT_NODE) {
|
||||
return null
|
||||
}
|
||||
const treeNodeProps = {
|
||||
...child.data,
|
||||
props: {
|
||||
...getAllProps(child),
|
||||
switcherIcon: props.switcherIcon,
|
||||
title: getComponentFromProp(child, 'title') || getComponentFromProp(child, 'label'),
|
||||
},
|
||||
key: String(child.key),
|
||||
}
|
||||
if (child && child.componentOptions.children) {
|
||||
// null or String has no Prop
|
||||
return (
|
||||
<TreeNode {...treeNodeProps}>
|
||||
{recursive(child.componentOptions.children) }
|
||||
</TreeNode>
|
||||
)
|
||||
}
|
||||
return <TreeNode {...treeNodeProps} />
|
||||
})
|
||||
}
|
||||
// const s = Date.now();
|
||||
let treeNodes
|
||||
if (props._cachetreeData && this.cacheTreeNodes) {
|
||||
treeNodes = this.cacheTreeNodes
|
||||
} else {
|
||||
treeNodes = recursive(props.treeData || props.treeNodes)
|
||||
this.cacheTreeNodes = treeNodes
|
||||
}
|
||||
// console.log(Date.now()-s);
|
||||
const {
|
||||
disabled, isMultiple,
|
||||
dropdownPopupAlign, dropdownMatchSelectWidth, dropdownClassName,
|
||||
dropdownStyle, dropdownVisibleChange, getPopupContainer,
|
||||
dropdownPrefixCls, popupElement, open,
|
||||
} = this.$props
|
||||
|
||||
if (props.inputValue) {
|
||||
treeNodes = this.processTreeNode(treeNodes)
|
||||
// TODO: [Legacy] Use new action when trigger fixed: https://github.com/react-component/trigger/pull/86
|
||||
|
||||
// When false do nothing with the width
|
||||
// ref: https://github.com/ant-design/ant-design/issues/10927
|
||||
let stretch
|
||||
if (dropdownMatchSelectWidth !== false) {
|
||||
stretch = dropdownMatchSelectWidth ? 'width' : 'minWidth'
|
||||
}
|
||||
|
||||
const keys = []
|
||||
const halfCheckedKeys = []
|
||||
loopAllChildren(treeNodes, (child) => {
|
||||
if (props.value.some(item => item.value === getValuePropValue(child))) {
|
||||
keys.push(String(getKey(child)))
|
||||
}
|
||||
if (props.halfCheckedValues &&
|
||||
props.halfCheckedValues.some(item => item.value === getValuePropValue(child))) {
|
||||
halfCheckedKeys.push(String(getKey(child)))
|
||||
}
|
||||
})
|
||||
|
||||
let notFoundContent
|
||||
if (!treeNodes.length) {
|
||||
if (props.notFoundContent) {
|
||||
notFoundContent = (
|
||||
<span class={`${props.prefixCls}-not-found`}>
|
||||
{props.notFoundContent}
|
||||
</span>
|
||||
)
|
||||
} else if (!search) {
|
||||
visible = false
|
||||
}
|
||||
}
|
||||
const popupElement = (
|
||||
<div>
|
||||
{search}
|
||||
{notFoundContent || this.renderTree(keys, halfCheckedKeys, treeNodes, multiple)}
|
||||
</div>
|
||||
)
|
||||
|
||||
const popupStyle = { ...props.dropdownStyle }
|
||||
const widthProp = props.dropdownMatchSelectWidth ? 'width' : 'minWidth'
|
||||
if (this.dropdownWidth) {
|
||||
popupStyle[widthProp] = `${this.dropdownWidth}px`
|
||||
}
|
||||
|
||||
return (
|
||||
<Trigger
|
||||
action={props.disabled ? [] : ['click']}
|
||||
ref='trigger'
|
||||
{...{ directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.triggerRef,
|
||||
}] }}
|
||||
action={disabled ? [] : ['click']}
|
||||
popupPlacement='bottomLeft'
|
||||
builtinPlacements={BUILT_IN_PLACEMENTS}
|
||||
popupAlign={props.dropdownPopupAlign}
|
||||
popupAlign={dropdownPopupAlign}
|
||||
prefixCls={dropdownPrefixCls}
|
||||
popupTransitionName={this.getDropdownTransitionName()}
|
||||
onPopupVisibleChange={props.dropdownVisibleChange}
|
||||
onPopupVisibleChange={dropdownVisibleChange}
|
||||
popup={popupElement}
|
||||
popupVisible={visible}
|
||||
getPopupContainer={props.getPopupContainer}
|
||||
popupClassName={classnames(popupClassName)}
|
||||
popupStyle={popupStyle}
|
||||
popupVisible={open}
|
||||
getPopupContainer={getPopupContainer}
|
||||
stretch={stretch}
|
||||
popupClassName={classNames(
|
||||
dropdownClassName,
|
||||
{
|
||||
[`${dropdownPrefixCls}--multiple`]: isMultiple,
|
||||
[`${dropdownPrefixCls}--single`]: !isMultiple,
|
||||
},
|
||||
)}
|
||||
popupStyle={dropdownStyle}
|
||||
>
|
||||
{this.$slots.default}
|
||||
</Trigger>
|
||||
|
@ -0,0 +1,64 @@
|
||||
|
||||
import PropTypes from '../../../../_util/vue-types'
|
||||
import {
|
||||
toTitle,
|
||||
UNSELECTABLE_ATTRIBUTE, UNSELECTABLE_STYLE,
|
||||
} from '../../util'
|
||||
import { getComponentFromProp } from '../../../../_util/props-util'
|
||||
import BaseMixin from '../../../../_util/BaseMixin'
|
||||
|
||||
const Selection = {
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
prefixCls: PropTypes.string,
|
||||
maxTagTextLength: PropTypes.number,
|
||||
// onRemove: PropTypes.func,
|
||||
|
||||
label: PropTypes.any,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
removeIcon: PropTypes.any,
|
||||
},
|
||||
methods: {
|
||||
onRemove (event) {
|
||||
const { value } = this.$props
|
||||
this.__emit('remove', event, value)
|
||||
event.stopPropagation()
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const {
|
||||
prefixCls, maxTagTextLength,
|
||||
label, value,
|
||||
} = this.$props
|
||||
const { $listeners } = this
|
||||
let content = label || value
|
||||
if (maxTagTextLength && typeof content === 'string' && content.length > maxTagTextLength) {
|
||||
content = `${content.slice(0, maxTagTextLength)}...`
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
style={UNSELECTABLE_STYLE}
|
||||
{...{ attrs: UNSELECTABLE_ATTRIBUTE }}
|
||||
role='menuitem'
|
||||
class={`${prefixCls}-selection__choice`}
|
||||
title={toTitle(label)}
|
||||
>
|
||||
{$listeners.remove &&
|
||||
<span
|
||||
class={`${prefixCls}-selection__choice__remove`}
|
||||
onClick={this.onRemove}
|
||||
>
|
||||
{getComponentFromProp(this, 'removeIcon')}
|
||||
</span>
|
||||
}
|
||||
<span class={`${prefixCls}-selection__choice__content`}>
|
||||
{content}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default Selection
|
@ -0,0 +1,192 @@
|
||||
import PropTypes from '../../../../_util/vue-types'
|
||||
import { createRef } from '../../util'
|
||||
import generateSelector, { selectorPropTypes } from '../../Base/BaseSelector'
|
||||
import SearchInput from '../../SearchInput'
|
||||
import Selection from './Selection'
|
||||
import { getComponentFromProp } from '../../../../_util/props-util'
|
||||
import getTransitionProps from '../../../../_util/getTransitionProps'
|
||||
import BaseMixin from '../../../../_util/BaseMixin'
|
||||
const TREE_SELECT_EMPTY_VALUE_KEY = 'RC_TREE_SELECT_EMPTY_VALUE_KEY'
|
||||
|
||||
const Selector = generateSelector('multiple')
|
||||
|
||||
// export const multipleSelectorContextTypes = {
|
||||
// onMultipleSelectorRemove: PropTypes.func.isRequired,
|
||||
// }
|
||||
|
||||
const MultipleSelector = {
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
...selectorPropTypes(),
|
||||
...SearchInput.props,
|
||||
selectorValueList: PropTypes.array,
|
||||
disabled: PropTypes.bool,
|
||||
searchValue: PropTypes.string,
|
||||
labelInValue: PropTypes.bool,
|
||||
maxTagCount: PropTypes.number,
|
||||
maxTagPlaceholder: PropTypes.any,
|
||||
|
||||
// onChoiceAnimationLeave: PropTypes.func,
|
||||
},
|
||||
inject: {
|
||||
vcTreeSelect: { default: {}},
|
||||
},
|
||||
created () {
|
||||
this.inputRef = createRef()
|
||||
},
|
||||
methods: {
|
||||
onPlaceholderClick () {
|
||||
this.inputRef.current.focus()
|
||||
},
|
||||
|
||||
focus () {
|
||||
this.inputRef.current.focus()
|
||||
},
|
||||
blur () {
|
||||
this.inputRef.current.blur()
|
||||
},
|
||||
|
||||
_renderPlaceholder () {
|
||||
const {
|
||||
prefixCls,
|
||||
placeholder, searchPlaceholder,
|
||||
searchValue, selectorValueList,
|
||||
} = this.$props
|
||||
|
||||
const currentPlaceholder = placeholder || searchPlaceholder
|
||||
|
||||
if (!currentPlaceholder) return null
|
||||
|
||||
const hidden = searchValue || selectorValueList.length
|
||||
|
||||
// [Legacy] Not remove the placeholder
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
display: hidden ? 'none' : 'block',
|
||||
}}
|
||||
onClick={this.onPlaceholderClick}
|
||||
class={`${prefixCls}-search__field__placeholder`}
|
||||
>
|
||||
{currentPlaceholder}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
onChoiceAnimationLeave (...args) {
|
||||
this.__emit('choiceAnimationLeave', ...args)
|
||||
},
|
||||
renderSelection () {
|
||||
const {
|
||||
selectorValueList, choiceTransitionName, prefixCls,
|
||||
labelInValue, maxTagCount,
|
||||
} = this.$props
|
||||
const { vcTreeSelect: { onMultipleSelectorRemove }, $listeners, $slots } = this
|
||||
|
||||
// Check if `maxTagCount` is set
|
||||
let myValueList = selectorValueList
|
||||
if (maxTagCount >= 0) {
|
||||
myValueList = selectorValueList.slice(0, maxTagCount)
|
||||
}
|
||||
// Selector node list
|
||||
const selectedValueNodes = myValueList.map(({ label, value }) => (
|
||||
<Selection
|
||||
{...{
|
||||
props: {
|
||||
...this.$props,
|
||||
label,
|
||||
value,
|
||||
},
|
||||
on: { ...$listeners, remove: onMultipleSelectorRemove },
|
||||
}}
|
||||
key={value || TREE_SELECT_EMPTY_VALUE_KEY}
|
||||
>{$slots.default}</Selection>
|
||||
))
|
||||
|
||||
// Rest node count
|
||||
if (maxTagCount >= 0 && maxTagCount < selectorValueList.length) {
|
||||
let content = `+ ${selectorValueList.length - maxTagCount} ...`
|
||||
const maxTagPlaceholder = getComponentFromProp(this, 'maxTagPlaceholder', {}, false)
|
||||
if (typeof maxTagPlaceholder === 'string') {
|
||||
content = maxTagPlaceholder
|
||||
} else if (typeof maxTagPlaceholder === 'function') {
|
||||
const restValueList = selectorValueList.slice(maxTagCount)
|
||||
content = maxTagPlaceholder(
|
||||
labelInValue ? restValueList : restValueList.map(({ value }) => value)
|
||||
)
|
||||
}
|
||||
|
||||
const restNodeSelect = (
|
||||
<Selection
|
||||
{...{
|
||||
props: {
|
||||
...this.$props,
|
||||
label: content,
|
||||
value: null,
|
||||
},
|
||||
on: $listeners,
|
||||
}}
|
||||
key='rc-tree-select-internal-max-tag-counter'
|
||||
>{$slots.default}</Selection>
|
||||
)
|
||||
|
||||
selectedValueNodes.push(restNodeSelect)
|
||||
}
|
||||
|
||||
selectedValueNodes.push(<li
|
||||
class={`${prefixCls}-search ${prefixCls}-search--inline`}
|
||||
key='__input'
|
||||
>
|
||||
<SearchInput {...{
|
||||
props: {
|
||||
...this.$props,
|
||||
needAlign: true,
|
||||
},
|
||||
on: $listeners,
|
||||
directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.inputRef,
|
||||
}],
|
||||
}}>{$slots.default}</SearchInput>
|
||||
</li>)
|
||||
const className = `${prefixCls}-selection__rendered`
|
||||
if (choiceTransitionName) {
|
||||
const transitionProps = getTransitionProps(choiceTransitionName, {
|
||||
tag: 'ul',
|
||||
afterLeave: this.onChoiceAnimationLeave,
|
||||
})
|
||||
return (<transition-group
|
||||
class={className}
|
||||
{...transitionProps}
|
||||
>
|
||||
{selectedValueNodes}
|
||||
</transition-group>)
|
||||
}
|
||||
return (
|
||||
<ul class={className} role='menubar'>
|
||||
{selectedValueNodes}
|
||||
</ul>
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const { $listeners, $slots } = this
|
||||
return (
|
||||
<Selector
|
||||
{...{
|
||||
props: {
|
||||
...this.$props,
|
||||
tabIndex: -1,
|
||||
showArrow: false,
|
||||
renderSelection: this.renderSelection,
|
||||
renderPlaceholder: this._renderPlaceholder,
|
||||
},
|
||||
on: $listeners,
|
||||
}}
|
||||
|
||||
>{$slots.default}</Selector>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default MultipleSelector
|
75
components/vc-tree-select/src/Selector/SingleSelector.jsx
Normal file
75
components/vc-tree-select/src/Selector/SingleSelector.jsx
Normal file
@ -0,0 +1,75 @@
|
||||
import generateSelector, { selectorPropTypes } from '../Base/BaseSelector'
|
||||
import { toTitle } from '../util'
|
||||
import { getOptionProps } from '../../../_util/props-util'
|
||||
import { createRef } from '../util'
|
||||
const Selector = generateSelector('single')
|
||||
|
||||
const SingleSelector = {
|
||||
name: 'SingleSelector',
|
||||
props: selectorPropTypes(),
|
||||
created () {
|
||||
this.selectorRef = createRef()
|
||||
},
|
||||
methods: {
|
||||
focus () {
|
||||
this.selectorRef.current.focus()
|
||||
},
|
||||
blur () {
|
||||
this.selectorRef.current.blur()
|
||||
},
|
||||
renderSelection () {
|
||||
const { selectorValueList, placeholder, prefixCls } = this.$props
|
||||
|
||||
let innerNode
|
||||
|
||||
if (selectorValueList.length) {
|
||||
const { label, value } = selectorValueList[0]
|
||||
innerNode = (
|
||||
<span
|
||||
key='value'
|
||||
title={toTitle(label)}
|
||||
class={`${prefixCls}-selection-selected-value`}
|
||||
>
|
||||
{label || value}
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
innerNode = (
|
||||
<span
|
||||
key='placeholder'
|
||||
class={`${prefixCls}-selection__placeholder`}
|
||||
>
|
||||
{placeholder}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<span class={`${prefixCls}-selection__rendered`}>
|
||||
{innerNode}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
render () {
|
||||
const props = {
|
||||
props: {
|
||||
...getOptionProps(this),
|
||||
renderSelection: this.renderSelection,
|
||||
},
|
||||
on: this.$listeners,
|
||||
directives: [{
|
||||
name: 'ant-ref',
|
||||
value: this.selectorRef,
|
||||
}],
|
||||
}
|
||||
return (
|
||||
<Selector
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default SingleSelector
|
@ -1,12 +0,0 @@
|
||||
import { TreeNode } from '../../vc-tree'
|
||||
export default {
|
||||
name: 'TreeNode',
|
||||
__ANT_TREE_SELECT_NODE: true,
|
||||
props: {
|
||||
...TreeNode.props,
|
||||
value: String,
|
||||
},
|
||||
render () {
|
||||
return this
|
||||
},
|
||||
}
|
@ -1,26 +1,7 @@
|
||||
// export this package's api
|
||||
import TreeSelect from './Select'
|
||||
import TreeNode from './TreeNode'
|
||||
import omit from 'omit.js'
|
||||
import { SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './strategies'
|
||||
TreeSelect.TreeNode = TreeNode
|
||||
import Select from './Select'
|
||||
import SelectNode from './SelectNode'
|
||||
|
||||
export default {
|
||||
functional: true,
|
||||
render (h, context) {
|
||||
const { props, listeners, children = [], data } = context
|
||||
const treeSelectProps = {
|
||||
...omit(data, ['attrs']),
|
||||
props: {
|
||||
...props,
|
||||
children,
|
||||
__propsSymbol__: Symbol(),
|
||||
},
|
||||
on: listeners,
|
||||
}
|
||||
return <TreeSelect {...treeSelectProps}/>
|
||||
},
|
||||
TreeNode,
|
||||
SHOW_ALL, SHOW_PARENT, SHOW_CHILD,
|
||||
}
|
||||
export { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD }
|
||||
export { SHOW_ALL, SHOW_CHILD, SHOW_PARENT } from './strategies'
|
||||
export const TreeNode = SelectNode
|
||||
|
||||
export default Select
|
||||
|
46
components/vc-tree-select/src/propTypes.js
Normal file
46
components/vc-tree-select/src/propTypes.js
Normal file
@ -0,0 +1,46 @@
|
||||
import PropTypes from '../../_util/vue-types'
|
||||
import { isLabelInValue } from './util'
|
||||
|
||||
const internalValProp = PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
])
|
||||
|
||||
export function genArrProps (propType) {
|
||||
return PropTypes.oneOfType([
|
||||
propType,
|
||||
PropTypes.arrayOf(propType),
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin code check `multiple` is true when `treeCheckStrictly` & `labelInValue`.
|
||||
* But in process logic is already cover to array.
|
||||
* Check array is not necessary. Let's simplify this check logic.
|
||||
*/
|
||||
export function valueProp (...args) {
|
||||
const [props, propName, Component] = args
|
||||
|
||||
if (isLabelInValue(props)) {
|
||||
const err = genArrProps(PropTypes.shape({
|
||||
label: PropTypes.node,
|
||||
value: internalValProp,
|
||||
}).loose)(...args)
|
||||
if (err) {
|
||||
return new Error(
|
||||
`Invalid prop \`${propName}\` supplied to \`${Component}\`. ` +
|
||||
`You should use { label: string, value: string | number } or [{ label: string, value: string | number }] instead.`
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const err = genArrProps(internalValProp)(...args)
|
||||
if (err) {
|
||||
return new Error(
|
||||
`Invalid prop \`${propName}\` supplied to \`${Component}\`. ` +
|
||||
`You should use string or [string] instead.`
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,5 +1,17 @@
|
||||
import { getPropsData, getAllProps, getKey, getAttrs, getSlotOptions, getSlots } from '../../_util/props-util'
|
||||
import { cloneVNodes, cloneElement } from '../../_util/vnode'
|
||||
import warning from 'warning'
|
||||
import omit from 'omit.js'
|
||||
import {
|
||||
convertDataToTree as vcConvertDataToTree,
|
||||
convertTreeToEntities as vcConvertTreeToEntities,
|
||||
conductCheck as rcConductCheck,
|
||||
} from '../../vc-tree/src/util'
|
||||
import SelectNode from './SelectNode'
|
||||
import { SHOW_CHILD, SHOW_PARENT } from './strategies'
|
||||
import { getSlots, getPropsData } from '../../_util/props-util'
|
||||
|
||||
let warnDeprecatedLabel = false
|
||||
|
||||
// =================== MISC ====================
|
||||
export function toTitle (title) {
|
||||
if (typeof title === 'string') {
|
||||
return title
|
||||
@ -7,58 +19,20 @@ export function toTitle (title) {
|
||||
return null
|
||||
}
|
||||
|
||||
export function getValuePropValue (child) {
|
||||
const props = getAllProps(child)
|
||||
if ('value' in props) {
|
||||
return props.value
|
||||
}
|
||||
if (getKey(child) !== undefined) {
|
||||
return getKey(child)
|
||||
}
|
||||
throw new Error(`no key or value for ${child}`)
|
||||
export function toArray (data) {
|
||||
if (!data) return []
|
||||
|
||||
return Array.isArray(data) ? data : [data]
|
||||
}
|
||||
|
||||
export function getPropValue (child, prop) {
|
||||
if (prop === 'value') {
|
||||
return getValuePropValue(child)
|
||||
}
|
||||
const slots = getSlots(child)
|
||||
if (prop === 'children') {
|
||||
const newChild = child.$slots ? cloneVNodes(child.$slots.default, true) : cloneVNodes(child.componentOptions.children, true)
|
||||
if (newChild.length === 1 && !newChild[0].tag) {
|
||||
return newChild[0].text
|
||||
}
|
||||
return newChild
|
||||
}
|
||||
if (slots[prop]) {
|
||||
return cloneVNodes(slots[prop], true)
|
||||
}
|
||||
const data = getPropsData(child)
|
||||
if (prop in data) {
|
||||
return data[prop]
|
||||
} else {
|
||||
return getAttrs(child)[prop]
|
||||
export function createRef () {
|
||||
const func = function setRef (node) {
|
||||
func.current = node
|
||||
}
|
||||
return func
|
||||
}
|
||||
|
||||
export function isMultiple (props) {
|
||||
return !!(props.multiple || props.treeCheckable)
|
||||
}
|
||||
|
||||
export function toArray (value) {
|
||||
let ret = value
|
||||
if (value === undefined) {
|
||||
ret = []
|
||||
} else if (!Array.isArray(value)) {
|
||||
ret = [value]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export function preventDefaultEvent (e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// =============== Legacy ===============
|
||||
export const UNSELECTABLE_STYLE = {
|
||||
userSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
@ -68,502 +42,389 @@ export const UNSELECTABLE_ATTRIBUTE = {
|
||||
unselectable: 'unselectable',
|
||||
}
|
||||
|
||||
export function labelCompatible (prop) {
|
||||
let newProp = prop
|
||||
if (newProp === 'label') {
|
||||
newProp = 'title'
|
||||
/**
|
||||
* Convert position list to hierarchy structure.
|
||||
* This is little hack since use '-' to split the position.
|
||||
*/
|
||||
export function flatToHierarchy (positionList) {
|
||||
if (!positionList.length) {
|
||||
return []
|
||||
}
|
||||
return newProp
|
||||
}
|
||||
|
||||
export function isInclude (smallArray, bigArray) {
|
||||
// attention: [0,0,1] [0,0,10]
|
||||
return smallArray.every((ii, i) => {
|
||||
return ii === bigArray[i]
|
||||
})
|
||||
}
|
||||
const entrances = {}
|
||||
|
||||
export function isPositionPrefix (smallPos, bigPos) {
|
||||
if (!bigPos || !smallPos) {
|
||||
// console.log(smallPos, bigPos);
|
||||
return false
|
||||
}
|
||||
if (bigPos.length < smallPos.length) {
|
||||
return false
|
||||
}
|
||||
// attention: "0-0-1" "0-0-10"
|
||||
if ((bigPos.length > smallPos.length) && (bigPos.charAt(smallPos.length) !== '-')) {
|
||||
return false
|
||||
}
|
||||
return bigPos.substr(0, smallPos.length) === smallPos
|
||||
}
|
||||
|
||||
/*
|
||||
export function getCheckedKeys(node, checkedKeys, allCheckedNodesKeys) {
|
||||
const nodeKey = node.props.eventKey;
|
||||
let newCks = [...checkedKeys];
|
||||
let nodePos;
|
||||
const unCheck = allCheckedNodesKeys.some(item => {
|
||||
if (item.key === nodeKey) {
|
||||
nodePos = item.pos;
|
||||
return true;
|
||||
// Prepare the position map
|
||||
const posMap = {}
|
||||
const parsedList = positionList.slice().map(entity => {
|
||||
const clone = {
|
||||
...entity,
|
||||
fields: entity.pos.split('-'),
|
||||
}
|
||||
});
|
||||
if (unCheck) {
|
||||
newCks = [];
|
||||
allCheckedNodesKeys.forEach(item => {
|
||||
if (isPositionPrefix(item.pos, nodePos) || isPositionPrefix(nodePos, item.pos)) {
|
||||
return;
|
||||
}
|
||||
newCks.push(item.key);
|
||||
});
|
||||
} else {
|
||||
newCks.push(nodeKey);
|
||||
}
|
||||
return newCks;
|
||||
}
|
||||
*/
|
||||
delete clone.children
|
||||
return clone
|
||||
})
|
||||
|
||||
function getChildrenlength (children) {
|
||||
let len = 1
|
||||
if (Array.isArray(children)) {
|
||||
len = children.length
|
||||
}
|
||||
return len
|
||||
parsedList.forEach((entity) => {
|
||||
posMap[entity.pos] = entity
|
||||
})
|
||||
|
||||
parsedList.sort((a, b) => {
|
||||
return a.fields.length - b.fields.length
|
||||
})
|
||||
|
||||
// Create the hierarchy
|
||||
parsedList.forEach((entity) => {
|
||||
const parentPos = entity.fields.slice(0, -1).join('-')
|
||||
const parentEntity = posMap[parentPos]
|
||||
|
||||
if (!parentEntity) {
|
||||
entrances[entity.pos] = entity
|
||||
} else {
|
||||
parentEntity.children = parentEntity.children || []
|
||||
parentEntity.children.push(entity)
|
||||
}
|
||||
|
||||
// Some time position list provide `key`, we don't need it
|
||||
delete entity.key
|
||||
delete entity.fields
|
||||
})
|
||||
|
||||
return Object.keys(entrances).map(key => entrances[key])
|
||||
}
|
||||
|
||||
function getSiblingPosition (index, len, siblingPosition) {
|
||||
if (len === 1) {
|
||||
siblingPosition.first = true
|
||||
siblingPosition.last = true
|
||||
} else {
|
||||
siblingPosition.first = index === 0
|
||||
siblingPosition.last = index === len - 1
|
||||
}
|
||||
return siblingPosition
|
||||
// =============== Accessibility ===============
|
||||
let ariaId = 0
|
||||
|
||||
export function resetAriaId () {
|
||||
ariaId = 0
|
||||
}
|
||||
|
||||
function filterChild (childs) {
|
||||
const newChilds = []
|
||||
childs.forEach(child => {
|
||||
const options = getSlotOptions(child)
|
||||
if (options.__ANT_TREE_NODE || options.__ANT_TREE_SELECT_NODE) {
|
||||
newChilds.push(child)
|
||||
export function generateAriaId (prefix) {
|
||||
ariaId += 1
|
||||
return `${prefix}_${ariaId}`
|
||||
}
|
||||
|
||||
export function isLabelInValue (props) {
|
||||
const { treeCheckable, treeCheckStrictly, labelInValue } = props
|
||||
if (treeCheckable && treeCheckStrictly) {
|
||||
return true
|
||||
}
|
||||
return labelInValue || false
|
||||
}
|
||||
|
||||
// =================== Tree ====================
|
||||
export function parseSimpleTreeData (treeData, { id, pId, rootPId }) {
|
||||
const keyNodes = {}
|
||||
const rootNodeList = []
|
||||
|
||||
// Fill in the map
|
||||
const nodeList = treeData.map((node) => {
|
||||
const clone = { ...node }
|
||||
const key = clone[id]
|
||||
keyNodes[key] = clone
|
||||
clone.key = clone.key || key
|
||||
return clone
|
||||
})
|
||||
|
||||
// Connect tree
|
||||
nodeList.forEach((node) => {
|
||||
const parentKey = node[pId]
|
||||
const parent = keyNodes[parentKey]
|
||||
|
||||
// Fill parent
|
||||
if (parent) {
|
||||
parent.children = parent.children || []
|
||||
parent.children.push(node)
|
||||
}
|
||||
|
||||
// Fill root tree node
|
||||
if (parentKey === rootPId || (!parent && rootPId === null)) {
|
||||
rootNodeList.push(node)
|
||||
}
|
||||
})
|
||||
return newChilds
|
||||
|
||||
return rootNodeList
|
||||
}
|
||||
|
||||
export function loopAllChildren (childs, callback, parent) {
|
||||
const loop = (children, level, _parent) => {
|
||||
const len = getChildrenlength(children)
|
||||
children.forEach(function handler(item, index) { // eslint-disable-line
|
||||
const pos = `${level}-${index}`
|
||||
if (item && item.componentOptions && item.componentOptions.children) {
|
||||
loop(filterChild(item.componentOptions.children), pos, { node: item, pos })
|
||||
}
|
||||
if (item) {
|
||||
callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent)
|
||||
/**
|
||||
* Detect if position has relation.
|
||||
* e.g. 1-2 related with 1-2-3
|
||||
* e.g. 1-3-2 related with 1
|
||||
* e.g. 1-2 not related with 1-21
|
||||
*/
|
||||
export function isPosRelated (pos1, pos2) {
|
||||
const fields1 = pos1.split('-')
|
||||
const fields2 = pos2.split('-')
|
||||
|
||||
const minLen = Math.min(fields1.length, fields2.length)
|
||||
for (let i = 0; i < minLen; i += 1) {
|
||||
if (fields1[i] !== fields2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is only used on treeNode check (none treeCheckStrictly but has searchInput).
|
||||
* We convert entity to { node, pos, children } format.
|
||||
* This is legacy bug but we still need to do with it.
|
||||
* @param entity
|
||||
*/
|
||||
export function cleanEntity ({ node, pos, children }) {
|
||||
const instance = {
|
||||
node,
|
||||
pos,
|
||||
}
|
||||
|
||||
if (children) {
|
||||
instance.children = children.map(cleanEntity)
|
||||
}
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a filtered TreeNode list by provided treeNodes.
|
||||
* [Legacy] Since `Tree` use `key` as map but `key` will changed by React,
|
||||
* we have to convert `treeNodes > data > treeNodes` to keep the key.
|
||||
* Such performance hungry!
|
||||
*/
|
||||
export function getFilterTree (h, treeNodes, searchValue, filterFunc, valueEntities) {
|
||||
if (!searchValue) {
|
||||
return null
|
||||
}
|
||||
|
||||
function mapFilteredNodeToData (node) {
|
||||
if (!node) return null
|
||||
|
||||
let match = false
|
||||
if (filterFunc(searchValue, node)) {
|
||||
match = true
|
||||
}
|
||||
const $slots = getSlots(node)
|
||||
const children = ($slots.default || []).map(mapFilteredNodeToData).filter(n => n)
|
||||
delete $slots.default
|
||||
const slotsKey = Object.keys($slots)
|
||||
if (children.length || match) {
|
||||
return (
|
||||
<SelectNode
|
||||
{...node.data}
|
||||
key={valueEntities[getPropsData(node).value].key}
|
||||
>
|
||||
{children}
|
||||
{slotsKey.length ? slotsKey.map(name => {
|
||||
return <template slot={name}>{$slots[name][0].tag === 'template' ? $slots[name][0].children : $slots[name]}</template>
|
||||
}) : null}
|
||||
</SelectNode>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return treeNodes.map(mapFilteredNodeToData).filter(node => node)
|
||||
}
|
||||
|
||||
// =================== Value ===================
|
||||
/**
|
||||
* Convert value to array format to make logic simplify.
|
||||
*/
|
||||
export function formatInternalValue (value, props) {
|
||||
const valueList = toArray(value)
|
||||
|
||||
// Parse label in value
|
||||
if (isLabelInValue(props)) {
|
||||
return valueList.map((val) => {
|
||||
if (typeof val !== 'object' || !val) {
|
||||
return {
|
||||
value: '',
|
||||
label: '',
|
||||
}
|
||||
}
|
||||
|
||||
return val
|
||||
})
|
||||
}
|
||||
loop(filterChild(childs), 0, parent)
|
||||
|
||||
return valueList.map(val => ({
|
||||
value: val,
|
||||
}))
|
||||
}
|
||||
|
||||
// export function loopAllChildren(childs, callback) {
|
||||
// const loop = (children, level) => {
|
||||
// React.Children.forEach(children, (item, index) => {
|
||||
// const pos = `${level}-${index}`;
|
||||
// if (item && item.props.children) {
|
||||
// loop(item.props.children, pos);
|
||||
// }
|
||||
// if (item) {
|
||||
// callback(item, index, pos, getValuePropValue(item));
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
// loop(childs, 0);
|
||||
// }
|
||||
|
||||
// TODO: Here has the side effect. Update node children data affect.
|
||||
export function flatToHierarchy (arr) {
|
||||
if (!arr.length) {
|
||||
return arr
|
||||
export function getLabel (wrappedValue, entity, treeNodeLabelProp) {
|
||||
if (wrappedValue.label) {
|
||||
return wrappedValue.label
|
||||
}
|
||||
const hierarchyNodes = []
|
||||
const levelObj = {}
|
||||
arr.forEach((item) => {
|
||||
if (!item.pos) {
|
||||
return
|
||||
|
||||
if (entity) {
|
||||
const props = getPropsData(entity.node)
|
||||
if (Object.keys(props).length) {
|
||||
return props[treeNodeLabelProp]
|
||||
}
|
||||
const posLen = item.pos.split('-').length
|
||||
if (!levelObj[posLen]) {
|
||||
levelObj[posLen] = []
|
||||
}
|
||||
levelObj[posLen].push(item)
|
||||
})
|
||||
const levelArr = Object.keys(levelObj).sort((a, b) => b - a)
|
||||
// const s = Date.now();
|
||||
// todo: there are performance issues!
|
||||
levelArr.reduce((pre, cur) => {
|
||||
if (cur && cur !== pre) {
|
||||
levelObj[pre].forEach((item) => {
|
||||
let haveParent = false
|
||||
levelObj[cur].forEach((ii) => {
|
||||
if (isPositionPrefix(ii.pos, item.pos)) {
|
||||
haveParent = true
|
||||
if (!ii.children) {
|
||||
ii.children = []
|
||||
}
|
||||
ii.children.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
// Since value without entity will be in missValueList.
|
||||
// This code will never reached, but we still need this in case.
|
||||
return wrappedValue.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert internal state `valueList` to user needed value list.
|
||||
* This will return an array list. You need check if is not multiple when return.
|
||||
*
|
||||
* `allCheckedNodes` is used for `treeCheckStrictly`
|
||||
*/
|
||||
export function formatSelectorValue (valueList, props, valueEntities) {
|
||||
const {
|
||||
treeNodeLabelProp,
|
||||
treeCheckable, treeCheckStrictly, showCheckedStrategy,
|
||||
} = props
|
||||
|
||||
// Will hide some value if `showCheckedStrategy` is set
|
||||
if (treeCheckable && !treeCheckStrictly) {
|
||||
const values = {}
|
||||
valueList.forEach((wrappedValue) => {
|
||||
values[wrappedValue.value] = wrappedValue
|
||||
})
|
||||
const hierarchyList = flatToHierarchy(valueList.map(({ value }) => valueEntities[value]))
|
||||
|
||||
if (showCheckedStrategy === SHOW_PARENT) {
|
||||
// Only get the parent checked value
|
||||
return hierarchyList.map(({ node }) => {
|
||||
const value = getPropsData(node).value
|
||||
return {
|
||||
label: getLabel(values[value], valueEntities[value], treeNodeLabelProp),
|
||||
value,
|
||||
}
|
||||
})
|
||||
} else if (showCheckedStrategy === SHOW_CHILD) {
|
||||
// Only get the children checked value
|
||||
const targetValueList = []
|
||||
|
||||
// Find the leaf children
|
||||
const traverse = ({ node, children }) => {
|
||||
const value = getPropsData(node).value
|
||||
if (!children || children.length === 0) {
|
||||
targetValueList.push({
|
||||
label: getLabel(values[value], valueEntities[value], treeNodeLabelProp),
|
||||
value,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
children.forEach((entity) => {
|
||||
traverse(entity)
|
||||
})
|
||||
if (!haveParent) {
|
||||
hierarchyNodes.push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
return cur
|
||||
})
|
||||
// console.log(Date.now() - s);
|
||||
return levelObj[levelArr[levelArr.length - 1]].concat(hierarchyNodes)
|
||||
}
|
||||
}
|
||||
|
||||
// arr.length === 628, use time: ~20ms
|
||||
export function filterParentPosition (arr) {
|
||||
const levelObj = {}
|
||||
arr.forEach((item) => {
|
||||
const posLen = item.split('-').length
|
||||
if (!levelObj[posLen]) {
|
||||
levelObj[posLen] = []
|
||||
}
|
||||
levelObj[posLen].push(item)
|
||||
})
|
||||
const levelArr = Object.keys(levelObj).sort()
|
||||
for (let i = 0; i < levelArr.length; i++) {
|
||||
if (levelArr[i + 1]) {
|
||||
levelObj[levelArr[i]].forEach(ii => {
|
||||
for (let j = i + 1; j < levelArr.length; j++) {
|
||||
levelObj[levelArr[j]].forEach((_i, index) => {
|
||||
if (isPositionPrefix(ii, _i)) {
|
||||
levelObj[levelArr[j]][index] = null
|
||||
}
|
||||
})
|
||||
levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p)
|
||||
}
|
||||
hierarchyList.forEach((entity) => {
|
||||
traverse(entity)
|
||||
})
|
||||
|
||||
return targetValueList
|
||||
}
|
||||
}
|
||||
let nArr = []
|
||||
levelArr.forEach(i => {
|
||||
nArr = nArr.concat(levelObj[i])
|
||||
})
|
||||
return nArr
|
||||
}
|
||||
// console.log(filterParentPosition(
|
||||
// ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1']
|
||||
// ));
|
||||
|
||||
function stripTail (str) {
|
||||
const arr = str.match(/(.+)(-[^-]+)$/)
|
||||
let st = ''
|
||||
if (arr && arr.length === 3) {
|
||||
st = arr[1]
|
||||
return valueList.map(wrappedValue => ({
|
||||
label: getLabel(wrappedValue, valueEntities[wrappedValue.value], treeNodeLabelProp),
|
||||
value: wrappedValue.value,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `rc-tree` convertDataToTree to convert treeData to TreeNodes.
|
||||
* This will change the label to title value
|
||||
*/
|
||||
function processProps (props) {
|
||||
const { title, label, key, value, class: cls, style, on = {}} = props
|
||||
const p = {
|
||||
props: omit(props, ['on', 'key', 'class', 'className', 'style']),
|
||||
on,
|
||||
class: cls || props.className,
|
||||
style: style,
|
||||
key: typeof key === 'number' ? String(key) : (key || value),
|
||||
}
|
||||
return st
|
||||
}
|
||||
function splitPosition (pos) {
|
||||
return pos.split('-')
|
||||
}
|
||||
|
||||
// todo: do optimization.
|
||||
export function handleCheckState (obj, checkedPositionArr, checkIt) {
|
||||
// console.log(stripTail('0-101-000'));
|
||||
// let s = Date.now();
|
||||
let objKeys = Object.keys(obj)
|
||||
|
||||
objKeys.forEach((i, index) => {
|
||||
const iArr = splitPosition(i)
|
||||
let saved = false
|
||||
checkedPositionArr.forEach((_pos) => {
|
||||
const _posArr = splitPosition(_pos)
|
||||
if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) {
|
||||
obj[i].halfChecked = false
|
||||
obj[i].checked = checkIt
|
||||
objKeys[index] = null
|
||||
}
|
||||
if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) {
|
||||
saved = true
|
||||
}
|
||||
})
|
||||
if (!saved) {
|
||||
objKeys[index] = null
|
||||
// Warning user not to use deprecated label prop.
|
||||
if (label && !title) {
|
||||
if (!warnDeprecatedLabel) {
|
||||
warning(
|
||||
false,
|
||||
'\'label\' in treeData is deprecated. Please use \'title\' instead.'
|
||||
)
|
||||
warnDeprecatedLabel = true
|
||||
}
|
||||
})
|
||||
objKeys = objKeys.filter(i => i) // filter non null;
|
||||
|
||||
for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
|
||||
// loop to set ancestral nodes's `checked` or `halfChecked`
|
||||
const loop = (__pos) => {
|
||||
const _posLen = splitPosition(__pos).length
|
||||
if (_posLen <= 2) { // e.g. '0-0', '0-1'
|
||||
return
|
||||
}
|
||||
let sibling = 0
|
||||
let siblingChecked = 0
|
||||
const parentPosition = stripTail(__pos)
|
||||
objKeys.forEach((i /* , index*/) => {
|
||||
const iArr = splitPosition(i)
|
||||
if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) {
|
||||
sibling++
|
||||
if (obj[i].checked) {
|
||||
siblingChecked++
|
||||
const _i = checkedPositionArr.indexOf(i)
|
||||
if (_i > -1) {
|
||||
checkedPositionArr.splice(_i, 1)
|
||||
if (_i <= pIndex) {
|
||||
pIndex--
|
||||
}
|
||||
}
|
||||
} else if (obj[i].halfChecked) {
|
||||
siblingChecked += 0.5
|
||||
}
|
||||
// objKeys[index] = null;
|
||||
}
|
||||
})
|
||||
// objKeys = objKeys.filter(i => i); // filter non null;
|
||||
const parent = obj[parentPosition]
|
||||
// not check, checked, halfChecked
|
||||
if (siblingChecked === 0) {
|
||||
parent.checked = false
|
||||
parent.halfChecked = false
|
||||
} else if (siblingChecked === sibling) {
|
||||
parent.checked = true
|
||||
parent.halfChecked = false
|
||||
} else {
|
||||
parent.halfChecked = true
|
||||
parent.checked = false
|
||||
}
|
||||
loop(parentPosition)
|
||||
}
|
||||
loop(checkedPositionArr[pIndex], pIndex)
|
||||
p.props.title = label
|
||||
}
|
||||
// console.log(Date.now()-s, objKeys.length, checkIt);
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
function getCheck (treeNodesStates, checkedPositions) {
|
||||
const halfCheckedKeys = []
|
||||
const checkedKeys = []
|
||||
const checkedNodes = []
|
||||
Object.keys(treeNodesStates).forEach((item) => {
|
||||
const itemObj = treeNodesStates[item]
|
||||
if (itemObj.checked) {
|
||||
checkedKeys.push(itemObj.key)
|
||||
// checkedNodes.push(getValuePropValue(itemObj.node));
|
||||
checkedNodes.push({ ...itemObj, pos: item })
|
||||
} else if (itemObj.halfChecked) {
|
||||
halfCheckedKeys.push(itemObj.key)
|
||||
}
|
||||
})
|
||||
export function convertDataToTree (h, treeData) {
|
||||
return vcConvertDataToTree(h, treeData, { processProps })
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `rc-tree` convertTreeToEntities for entities calculation.
|
||||
* We have additional entities of `valueEntities`
|
||||
*/
|
||||
function initWrapper (wrapper) {
|
||||
return {
|
||||
halfCheckedKeys, checkedKeys, checkedNodes, treeNodesStates, checkedPositions,
|
||||
...wrapper,
|
||||
valueEntities: {},
|
||||
}
|
||||
}
|
||||
|
||||
export function getTreeNodesStates (children, values) {
|
||||
const checkedPositions = []
|
||||
const treeNodesStates = {}
|
||||
loopAllChildren(children, (item, index, pos, keyOrPos, siblingPosition) => {
|
||||
treeNodesStates[pos] = {
|
||||
node: item,
|
||||
key: keyOrPos,
|
||||
checked: false,
|
||||
halfChecked: false,
|
||||
siblingPosition,
|
||||
}
|
||||
if (values.indexOf(getValuePropValue(item)) !== -1) {
|
||||
treeNodesStates[pos].checked = true
|
||||
checkedPositions.push(pos)
|
||||
}
|
||||
})
|
||||
function processEntity (entity, wrapper) {
|
||||
const value = getPropsData(entity.node).value
|
||||
entity.value = value
|
||||
|
||||
handleCheckState(treeNodesStates, filterParentPosition(checkedPositions.sort()), true)
|
||||
|
||||
return getCheck(treeNodesStates, checkedPositions)
|
||||
// This should be empty, or will get error message.
|
||||
const currentEntity = wrapper.valueEntities[value]
|
||||
if (currentEntity) {
|
||||
warning(
|
||||
false,
|
||||
`Conflict! value of node '${entity.key}' (${value}) has already used by node '${currentEntity.key}'.`
|
||||
)
|
||||
}
|
||||
wrapper.valueEntities[value] = entity
|
||||
}
|
||||
|
||||
// can add extra prop to every node.
|
||||
export function recursiveCloneChildren (children, cb = ch => ch) {
|
||||
// return React.Children.map(children, child => {
|
||||
return Array.from(children).map(child => {
|
||||
const newChild = cb(child)
|
||||
if (newChild && newChild.props && newChild.props.children) {
|
||||
return cloneElement(newChild, {
|
||||
children: recursiveCloneChildren(newChild.props.children, cb),
|
||||
})
|
||||
}
|
||||
return newChild
|
||||
})
|
||||
}
|
||||
// const newChildren = recursiveCloneChildren(children, child => {
|
||||
// const extraProps = {};
|
||||
// if (child && child.type && child.type.xxx) {
|
||||
// extraProps._prop = true;
|
||||
// return React.cloneElement(child, extraProps);
|
||||
// }
|
||||
// return child;
|
||||
// });
|
||||
|
||||
function recursiveGen (children, level = 0) {
|
||||
return children.map((child, index) => {
|
||||
const pos = `${level}-${index}`
|
||||
const props = getAllProps(child)
|
||||
const { title, label, value, ...rest } = props
|
||||
const { children: subChildren } = child.componentOptions
|
||||
const o = {
|
||||
...rest,
|
||||
title,
|
||||
label: label || title,
|
||||
value,
|
||||
key: child.key,
|
||||
_pos: pos,
|
||||
}
|
||||
if (subChildren) {
|
||||
o.children = recursiveGen(subChildren, pos)
|
||||
}
|
||||
return o
|
||||
export function convertTreeToEntities (treeNodes) {
|
||||
return vcConvertTreeToEntities(treeNodes, {
|
||||
initWrapper,
|
||||
processEntity,
|
||||
})
|
||||
}
|
||||
|
||||
function recursive (children, cb) {
|
||||
children.forEach(item => {
|
||||
cb(item)
|
||||
if (item.children) {
|
||||
recursive(item.children, cb)
|
||||
/**
|
||||
* https://github.com/ant-design/ant-design/issues/13328
|
||||
* We need calculate the half check key when searchValue is set.
|
||||
*/
|
||||
// TODO: This logic may better move to rc-tree
|
||||
export function getHalfCheckedKeys (valueList, valueEntities) {
|
||||
const values = {}
|
||||
|
||||
// Fill checked keys
|
||||
valueList.forEach(({ value }) => {
|
||||
values[value] = false
|
||||
})
|
||||
|
||||
// Fill half checked keys
|
||||
valueList.forEach(({ value }) => {
|
||||
let current = valueEntities[value]
|
||||
|
||||
while (current && current.parent) {
|
||||
const parentValue = current.parent.value
|
||||
if (parentValue in values) break
|
||||
values[parentValue] = true
|
||||
|
||||
current = current.parent
|
||||
}
|
||||
})
|
||||
|
||||
// Get half keys
|
||||
return Object.keys(values).filter(value => values[value]).map(value => valueEntities[value].key)
|
||||
}
|
||||
|
||||
// Get the tree's checkedNodes (todo: can merge to the `handleCheckState` function)
|
||||
// If one node checked, it's all children nodes checked.
|
||||
// If sibling nodes all checked, the parent checked.
|
||||
export function filterAllCheckedData (vs, treeNodes) {
|
||||
const vals = [...vs]
|
||||
if (!vals.length) {
|
||||
return vals
|
||||
}
|
||||
|
||||
const data = recursiveGen(treeNodes)
|
||||
const checkedNodesPositions = []
|
||||
|
||||
function checkChildren (children) {
|
||||
children.forEach(item => {
|
||||
if (item.__checked) {
|
||||
return
|
||||
}
|
||||
const ci = vals.indexOf(item.value)
|
||||
const childs = item.children
|
||||
if (ci > -1) {
|
||||
item.__checked = true
|
||||
checkedNodesPositions.push({ node: item, pos: item._pos })
|
||||
vals.splice(ci, 1)
|
||||
if (childs) {
|
||||
recursive(childs, child => {
|
||||
child.__checked = true
|
||||
checkedNodesPositions.push({ node: child, pos: child._pos })
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (childs) {
|
||||
checkChildren(childs)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function checkParent (children, parent = { root: true }) {
|
||||
let siblingChecked = 0
|
||||
children.forEach(item => {
|
||||
const childs = item.children
|
||||
if (childs && !item.__checked && !item.__halfChecked) {
|
||||
const p = checkParent(childs, item)
|
||||
if (p.__checked) {
|
||||
siblingChecked++
|
||||
} else if (p.__halfChecked) {
|
||||
siblingChecked += 0.5
|
||||
}
|
||||
} else if (item.__checked) {
|
||||
siblingChecked++
|
||||
} else if (item.__halfChecked) {
|
||||
siblingChecked += 0.5
|
||||
}
|
||||
})
|
||||
const len = children.length
|
||||
if (siblingChecked === len) {
|
||||
parent.__checked = true
|
||||
checkedNodesPositions.push({ node: parent, pos: parent._pos })
|
||||
} else if (siblingChecked < len && siblingChecked > 0) {
|
||||
parent.__halfChecked = true
|
||||
}
|
||||
if (parent.root) {
|
||||
return children
|
||||
}
|
||||
return parent
|
||||
}
|
||||
checkChildren(data)
|
||||
checkParent(data)
|
||||
|
||||
checkedNodesPositions.forEach((i, index) => {
|
||||
// clear private metadata
|
||||
delete checkedNodesPositions[index].node.__checked
|
||||
delete checkedNodesPositions[index].node._pos
|
||||
// create the same structure of `onCheck`'s return.
|
||||
checkedNodesPositions[index].node.props = {
|
||||
title: checkedNodesPositions[index].node.title,
|
||||
label: checkedNodesPositions[index].node.label || checkedNodesPositions[index].node.title,
|
||||
value: checkedNodesPositions[index].node.value,
|
||||
}
|
||||
if (checkedNodesPositions[index].node.children) {
|
||||
checkedNodesPositions[index].node.props.children = checkedNodesPositions[index].node.children
|
||||
}
|
||||
delete checkedNodesPositions[index].node.title
|
||||
delete checkedNodesPositions[index].node.label
|
||||
delete checkedNodesPositions[index].node.value
|
||||
delete checkedNodesPositions[index].node.children
|
||||
})
|
||||
return checkedNodesPositions
|
||||
}
|
||||
|
||||
export function processSimpleTreeData (treeData, format) {
|
||||
function unflatten2 (array, parent = { [format.id]: format.rootPId }) {
|
||||
const children = []
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
array[i] = { ...array[i] } // copy, can not corrupts original data
|
||||
if (array[i][format.pId] === parent[format.id]) {
|
||||
array[i].key = array[i][format.id]
|
||||
children.push(array[i])
|
||||
array.splice(i--, 1)
|
||||
}
|
||||
}
|
||||
if (children.length) {
|
||||
parent.children = children
|
||||
children.forEach(child => unflatten2(array, child))
|
||||
}
|
||||
if (parent[format.id] === format.rootPId) {
|
||||
return children
|
||||
}
|
||||
}
|
||||
return unflatten2(treeData)
|
||||
}
|
||||
|
||||
export function saveRef (instance, name) {
|
||||
if (!instance.saveRefs) {
|
||||
instance.saveRefs = {}
|
||||
}
|
||||
if (!instance.saveRefs[name]) {
|
||||
instance.saveRefs[name] = (node) => {
|
||||
instance[name] = node
|
||||
}
|
||||
}
|
||||
return instance.saveRefs[name]
|
||||
}
|
||||
export const conductCheck = rcConductCheck
|
||||
|
@ -101,6 +101,8 @@ const Tree = {
|
||||
}),
|
||||
|
||||
data () {
|
||||
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__')
|
||||
warning(this.$props.children, 'please children prop replace slots.default')
|
||||
this.needSyncKeys = {}
|
||||
const state = {
|
||||
_posEntities: {},
|
||||
@ -119,12 +121,6 @@ const Tree = {
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
// ...this.getSyncProps(props),
|
||||
// dragOverNodeKey: '',
|
||||
// dropPosition: null,
|
||||
// dragNodesKeys: [],
|
||||
// sLoadedKeys: [],
|
||||
// sLoadingKeys: [],
|
||||
...this.getDerivedStateFromProps(getOptionProps(this), state),
|
||||
}
|
||||
},
|
||||
@ -478,7 +474,7 @@ const Tree = {
|
||||
event: 'load',
|
||||
node: treeNode,
|
||||
}
|
||||
this.__emit('load', eventObj)
|
||||
this.__emit('load', newLoadedKeys, eventObj)
|
||||
this.setUncontrolledState({
|
||||
_loadedKeys: newLoadedKeys,
|
||||
})
|
||||
@ -593,7 +589,6 @@ const Tree = {
|
||||
|
||||
return cloneElement(child, {
|
||||
props: {
|
||||
key,
|
||||
eventKey: key,
|
||||
expanded: expandedKeys.indexOf(key) !== -1,
|
||||
selected: selectedKeys.indexOf(key) !== -1,
|
||||
@ -608,6 +603,7 @@ const Tree = {
|
||||
dragOverGapTop: dragOverNodeKey === key && dropPosition === -1,
|
||||
dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1,
|
||||
},
|
||||
key,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
@ -46,6 +46,9 @@ const TreeNode = {
|
||||
icon: PropTypes.any,
|
||||
dataRef: PropTypes.object,
|
||||
switcherIcon: PropTypes.any,
|
||||
|
||||
label: PropTypes.any,
|
||||
value: PropTypes.any,
|
||||
}, {}),
|
||||
|
||||
data () {
|
||||
|
@ -4,8 +4,10 @@ import Align from '../vc-align'
|
||||
import PopupInner from './PopupInner'
|
||||
import LazyRenderBox from './LazyRenderBox'
|
||||
import animate from '../_util/css-animation'
|
||||
import BaseMixin from '../_util/BaseMixin'
|
||||
|
||||
export default {
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
visible: PropTypes.bool,
|
||||
getClassNameFromAlign: PropTypes.func,
|
||||
@ -165,7 +167,7 @@ export default {
|
||||
|
||||
// Delay force align to makes ui smooth
|
||||
if (!stretchChecked) {
|
||||
sizeStyle.visibility = 'hidden'
|
||||
// sizeStyle.visibility = 'hidden'
|
||||
setTimeout(() => {
|
||||
if (this.$refs.alignInstance) {
|
||||
this.$refs.alignInstance.forceAlign()
|
||||
|
Loading…
Reference in New Issue
Block a user