feat: add tree-select
parent
c0fb36468f
commit
7cd4f39756
|
@ -117,6 +117,16 @@ const getComponentFromProp = (instance, prop) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAllProps = (ele) => {
|
||||||
|
let data = ele.data || {}
|
||||||
|
let componentOptions = ele.componentOptions || {}
|
||||||
|
if (ele.$vnode) {
|
||||||
|
data = ele.$vnode.data || {}
|
||||||
|
componentOptions = ele.$vnode.componentOptions || {}
|
||||||
|
}
|
||||||
|
return { ...data.props, ...data.attrs, ...componentOptions.propsData }
|
||||||
|
}
|
||||||
|
|
||||||
const getPropsData = (ele) => {
|
const getPropsData = (ele) => {
|
||||||
let componentOptions = ele.componentOptions
|
let componentOptions = ele.componentOptions
|
||||||
if (ele.$vnode) {
|
if (ele.$vnode) {
|
||||||
|
@ -247,5 +257,6 @@ export {
|
||||||
isValidElement,
|
isValidElement,
|
||||||
camelize,
|
camelize,
|
||||||
getSlots,
|
getSlots,
|
||||||
|
getAllProps,
|
||||||
}
|
}
|
||||||
export default hasProp
|
export default hasProp
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { getComponentFromProp, getOptionProps, filterEmpty, isValidElement } fro
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const AutoCompleteProps = {
|
const AutoCompleteProps = {
|
||||||
...AbstractSelectProps,
|
...AbstractSelectProps(),
|
||||||
value: SelectValue,
|
value: SelectValue,
|
||||||
defaultValue: SelectValue,
|
defaultValue: SelectValue,
|
||||||
dataSource: PropTypes.array,
|
dataSource: PropTypes.array,
|
||||||
|
|
|
@ -102,7 +102,7 @@ import { default as Transfer } from './transfer'
|
||||||
|
|
||||||
import { default as Tree } from './tree'
|
import { default as Tree } from './tree'
|
||||||
|
|
||||||
// import { default as TreeSelect } from './tree-select'
|
import { default as TreeSelect } from './tree-select'
|
||||||
|
|
||||||
import { default as Tabs } from './tabs'
|
import { default as Tabs } from './tabs'
|
||||||
|
|
||||||
|
@ -196,6 +196,8 @@ const components = [
|
||||||
Transfer,
|
Transfer,
|
||||||
Tree,
|
Tree,
|
||||||
Tree.TreeNode,
|
Tree.TreeNode,
|
||||||
|
TreeSelect,
|
||||||
|
TreeSelect.TreeNode,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tabs.TabPane,
|
Tabs.TabPane,
|
||||||
Tag,
|
Tag,
|
||||||
|
@ -269,6 +271,7 @@ export {
|
||||||
Table,
|
Table,
|
||||||
Transfer,
|
Transfer,
|
||||||
Tree,
|
Tree,
|
||||||
|
TreeSelect,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tag,
|
Tag,
|
||||||
TimePicker,
|
TimePicker,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver'
|
||||||
import defaultLocale from '../locale-provider/default'
|
import defaultLocale from '../locale-provider/default'
|
||||||
import { getComponentFromProp, getOptionProps, filterEmpty } from '../_util/props-util'
|
import { getComponentFromProp, getOptionProps, filterEmpty } from '../_util/props-util'
|
||||||
|
|
||||||
const AbstractSelectProps = {
|
const AbstractSelectProps = () => ({
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: PropTypes.string,
|
||||||
size: PropTypes.oneOf(['small', 'large', 'default']),
|
size: PropTypes.oneOf(['small', 'large', 'default']),
|
||||||
notFoundContent: PropTypes.any,
|
notFoundContent: PropTypes.any,
|
||||||
|
@ -28,7 +28,7 @@ const AbstractSelectProps = {
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus: PropTypes.bool,
|
||||||
backfill: PropTypes.bool,
|
backfill: PropTypes.bool,
|
||||||
showArrow: PropTypes.bool,
|
showArrow: PropTypes.bool,
|
||||||
}
|
})
|
||||||
const Value = PropTypes.shape({
|
const Value = PropTypes.shape({
|
||||||
key: PropTypes.string,
|
key: PropTypes.string,
|
||||||
}).loose
|
}).loose
|
||||||
|
@ -45,7 +45,7 @@ const SelectValue = PropTypes.oneOfType([
|
||||||
])
|
])
|
||||||
|
|
||||||
const SelectProps = {
|
const SelectProps = {
|
||||||
...AbstractSelectProps,
|
...AbstractSelectProps(),
|
||||||
value: SelectValue,
|
value: SelectValue,
|
||||||
defaultValue: SelectValue,
|
defaultValue: SelectValue,
|
||||||
mode: PropTypes.oneOf(['default', 'multiple', 'tags', 'combobox']),
|
mode: PropTypes.oneOf(['default', 'multiple', 'tags', 'combobox']),
|
||||||
|
|
|
@ -46,3 +46,4 @@ import './layout/style'
|
||||||
import './form/style'
|
import './form/style'
|
||||||
import './anchor/style'
|
import './anchor/style'
|
||||||
import './list/style'
|
import './list/style'
|
||||||
|
import './tree-select/style'
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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 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;"><b></b></span></span>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/tree-select/demo/checkable.md correctly 1`] = `
|
||||||
|
<span class="ant-select ant-select-enabled 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"></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>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/tree-select/demo/multiple.md correctly 1`] = `
|
||||||
|
<span class="ant-select ant-select-enabled ant-select-allow-clear 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>
|
||||||
|
</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 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;"><b></b></span></span>
|
||||||
|
</span>
|
||||||
|
`;
|
|
@ -0,0 +1,3 @@
|
||||||
|
import demoTest from '../../../tests/shared/demoTest'
|
||||||
|
|
||||||
|
demoTest('tree-select')
|
|
@ -0,0 +1,6 @@
|
||||||
|
import TreeSelect from '..'
|
||||||
|
import focusTest from '../../../tests/shared/focusTest'
|
||||||
|
|
||||||
|
describe('TreeSelect', () => {
|
||||||
|
focusTest(TreeSelect)
|
||||||
|
})
|
|
@ -0,0 +1,53 @@
|
||||||
|
<cn>
|
||||||
|
#### 基本用法
|
||||||
|
最简单的用法。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Basic
|
||||||
|
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-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(arguments)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -0,0 +1,73 @@
|
||||||
|
<cn>
|
||||||
|
#### 可勾选
|
||||||
|
使用勾选框实现多选功能。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Checkable
|
||||||
|
Multiple and checkable.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<a-tree-select
|
||||||
|
style="width: 300px"
|
||||||
|
:treeData="treeData"
|
||||||
|
:value="value"
|
||||||
|
@change="onChange"
|
||||||
|
treeCheckable
|
||||||
|
:showCheckedStrategy="SHOW_PARENT"
|
||||||
|
searchPlaceholder='Please select'
|
||||||
|
treeNodeFilterProp='label'
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { TreeSelect } from 'vue-antd-ui'
|
||||||
|
const SHOW_PARENT = TreeSelect.SHOW_PARENT
|
||||||
|
|
||||||
|
const treeData = [{
|
||||||
|
label: 'Node1',
|
||||||
|
value: '0-0',
|
||||||
|
key: '0-0',
|
||||||
|
children: [{
|
||||||
|
label: 'Child Node1',
|
||||||
|
value: '0-0-0',
|
||||||
|
key: '0-0-0',
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
label: 'Node2',
|
||||||
|
value: '0-1',
|
||||||
|
key: '0-1',
|
||||||
|
children: [{
|
||||||
|
label: 'Child Node3',
|
||||||
|
value: '0-1-0',
|
||||||
|
key: '0-1-0',
|
||||||
|
disabled: true,
|
||||||
|
}, {
|
||||||
|
label: 'Child Node4',
|
||||||
|
value: '0-1-1',
|
||||||
|
key: '0-1-1',
|
||||||
|
}, {
|
||||||
|
label: 'Child Node5',
|
||||||
|
value: '0-1-2',
|
||||||
|
key: '0-1-2',
|
||||||
|
}],
|
||||||
|
}]
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
value: ['0-0-0'],
|
||||||
|
treeData,
|
||||||
|
SHOW_PARENT,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange (value) {
|
||||||
|
console.log('onChange ', value, arguments)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script>
|
||||||
|
import Basic from './basic'
|
||||||
|
import Checkable from './checkable'
|
||||||
|
import Multiple from './multiple'
|
||||||
|
import TreeData from './treeData'
|
||||||
|
|
||||||
|
import CN from '../index.zh-CN.md'
|
||||||
|
import US from '../index.en-US.md'
|
||||||
|
|
||||||
|
const md = {
|
||||||
|
cn: `# 树型选择控件。
|
||||||
|
## 何时使用
|
||||||
|
|
||||||
|
类似 Select 的选择控件,可选择的数据结构是一个树形结构时,可以使用 TreeSelect,例如公司层级、学科系统、分类目录等等。
|
||||||
|
|
||||||
|
## 代码演示`,
|
||||||
|
us: `# TreeSelect
|
||||||
|
## When To Use
|
||||||
|
|
||||||
|
\`TreeSelect\` is similar to \`Select\`, but the values are provided in a tree like structure.
|
||||||
|
Any data whose entries are defined in a hierarchical manner is fit to use this control. Examples of such case may include a corporate hierarchy, a directory structure, and so on.
|
||||||
|
## Examples
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
category: 'Components',
|
||||||
|
subtitle: '树选择',
|
||||||
|
type: 'Data Entry',
|
||||||
|
title: 'TreeSelect',
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<md cn={md.cn} us={md.us}/>
|
||||||
|
<Basic/>
|
||||||
|
<Checkable/>
|
||||||
|
<Multiple/>
|
||||||
|
<TreeData/>
|
||||||
|
<api>
|
||||||
|
<template slot='cn'>
|
||||||
|
<CN/>
|
||||||
|
</template>
|
||||||
|
<US/>
|
||||||
|
</api>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<cn>
|
||||||
|
#### 多选
|
||||||
|
多选的树选择。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Multiple Selection
|
||||||
|
Multiple selection usage.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<a-tree-select
|
||||||
|
showSearch
|
||||||
|
style="width: 300px"
|
||||||
|
:value="value"
|
||||||
|
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
placeholder='Please select'
|
||||||
|
allowClear
|
||||||
|
multiple
|
||||||
|
treeDefaultExpandAll
|
||||||
|
@change="onChange"
|
||||||
|
@search="onSearch"
|
||||||
|
@select="onSelect"
|
||||||
|
>
|
||||||
|
<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(arguments)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
onSearch () {
|
||||||
|
console.log(arguments)
|
||||||
|
},
|
||||||
|
onSelect () {
|
||||||
|
console.log(arguments)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -0,0 +1,63 @@
|
||||||
|
<cn>
|
||||||
|
#### 从数据直接生成
|
||||||
|
使用 `treeData` 把 JSON 数据直接生成树结构。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Generate form tree data
|
||||||
|
The tree structure can be populated using `treeData` property. This is a quick and easy way to provide the tree content.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<a-tree-select
|
||||||
|
style="width: 300px"
|
||||||
|
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
: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'">
|
||||||
|
<a-icon type="home"/>Child Node1 {{value}}
|
||||||
|
</span>
|
||||||
|
</a-tree-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const treeData = [{
|
||||||
|
label: 'Node1',
|
||||||
|
value: '0-0',
|
||||||
|
key: '0-0',
|
||||||
|
children: [{
|
||||||
|
value: '0-0-1',
|
||||||
|
key: '0-0-1',
|
||||||
|
scopedSlots: { // custom label
|
||||||
|
label: 'label',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
label: 'Child Node2',
|
||||||
|
value: '0-0-2',
|
||||||
|
key: '0-0-2',
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
label: 'Node2',
|
||||||
|
value: '0-1',
|
||||||
|
key: '0-1',
|
||||||
|
}]
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
value: undefined,
|
||||||
|
treeData,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value (value) {
|
||||||
|
console.log(value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -0,0 +1,61 @@
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Tree props
|
||||||
|
|
||||||
|
| Property | Description | Type | Default |
|
||||||
|
| -------- | ----------- | ---- | ------- |
|
||||||
|
| allowClear | Whether allow clear | boolean | false |
|
||||||
|
| defaultValue | To set the initial selected treeNode(s). | string\|string\[] | - |
|
||||||
|
| disabled | Disabled or not | boolean | false |
|
||||||
|
| dropdownClassName | className of dropdown menu | string | - |
|
||||||
|
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width | boolean | true |
|
||||||
|
| dropdownStyle | To set the style of the dropdown menu | object | - |
|
||||||
|
| filterTreeNode | Whether to filter treeNodes by input value. The value of `treeNodeFilterProp` is used for filtering by default. | boolean\|Function(inputValue: string, treeNode: TreeNode) (should return boolean) | Function |
|
||||||
|
| getPopupContainer | To set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | () => document.body |
|
||||||
|
| labelInValue | whether to embed label in value, turn the format of value from `string` to `{value: string, label: VNode, halfChecked: string[]}` | boolean | false |
|
||||||
|
| loadData | Load data asynchronously. | function(node) | - |
|
||||||
|
| multiple | Support multiple or not, will be `true` when enable `treeCheckable`. | boolean | false |
|
||||||
|
| placeholder | Placeholder of the select input | string\|slot | - |
|
||||||
|
| searchPlaceholder | Placeholder of the search input | string\|slot | - |
|
||||||
|
| 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' |
|
||||||
|
| 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\[] | - |
|
||||||
|
| 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\[] | - |
|
||||||
|
|
||||||
|
|
||||||
|
### Events
|
||||||
|
| Events Name | Description | Arguments |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 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) |
|
||||||
|
|
||||||
|
### Tree Methods
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
| blur() | remove focus |
|
||||||
|
| focus() | get focus |
|
||||||
|
|
||||||
|
### TreeNode props
|
||||||
|
|
||||||
|
> We recommend you to use `treeData` rather than `TreeNode`, to avoid the trouble of manual construction.
|
||||||
|
|
||||||
|
| Property | Description | Type | Default |
|
||||||
|
| -------- | ----------- | ---- | ------- |
|
||||||
|
| disableCheckbox | Disables the checkbox of the treeNode | boolean | false |
|
||||||
|
| disabled | Disabled or not | boolean | false |
|
||||||
|
| isLeaf | Leaf node or not | boolean | false |
|
||||||
|
| key | Required property, should be unique in the tree | string | - |
|
||||||
|
| title | Content showed on the treeNodes | string\|slot | '---' |
|
||||||
|
| value | Will be treated as `treeNodeFilterProp` by default, should be unique in the tree | string | - |
|
||||||
|
| scopedSlots | When using treeNodes, you can use this property to configure the properties that support the slot, such as `scopedSlots: { title: 'XXX'}` | object | - |
|
|
@ -0,0 +1,115 @@
|
||||||
|
|
||||||
|
import VcTreeSelect, { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from '../vc-tree-select'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import { TreeSelectProps } from './interface'
|
||||||
|
import LocaleReceiver from '../locale-provider/LocaleReceiver'
|
||||||
|
import warning from '../_util/warning'
|
||||||
|
import { initDefaultProps, getOptionProps, getComponentFromProp, filterEmpty } from '../_util/props-util'
|
||||||
|
|
||||||
|
export { TreeData, TreeSelectProps } from './interface'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
TreeNode: { ...TreeNode, name: 'ATreeSelectNode' },
|
||||||
|
SHOW_ALL,
|
||||||
|
SHOW_PARENT,
|
||||||
|
SHOW_CHILD,
|
||||||
|
name: 'ATreeSelect',
|
||||||
|
props: initDefaultProps(TreeSelectProps(), {
|
||||||
|
prefixCls: 'ant-select',
|
||||||
|
transitionName: 'slide-up',
|
||||||
|
choiceTransitionName: 'zoom',
|
||||||
|
showSearch: false,
|
||||||
|
}),
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change',
|
||||||
|
},
|
||||||
|
|
||||||
|
created () {
|
||||||
|
warning(
|
||||||
|
this.multiple !== false || !this.treeCheckable,
|
||||||
|
'`multiple` will alway be `true` when `treeCheckable` is true',
|
||||||
|
)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focus () {
|
||||||
|
this.$refs.vcTreeSelect.focus()
|
||||||
|
},
|
||||||
|
|
||||||
|
blur () {
|
||||||
|
this.$refs.vcTreeSelect.blur()
|
||||||
|
},
|
||||||
|
onChange () {
|
||||||
|
this.$emit('change', ...arguments)
|
||||||
|
},
|
||||||
|
updateTreeData (list = []) {
|
||||||
|
for (let i = 0, len = list.length; i < len; i++) {
|
||||||
|
const { label, title, scopedSlots = {}, children } = list[i]
|
||||||
|
const { $scopedSlots } = this
|
||||||
|
let newLabel = typeof label === 'function' ? label(this.$createElement) : label
|
||||||
|
let newTitle = typeof title === 'function' ? title(this.$createElement) : title
|
||||||
|
if (!newLabel && scopedSlots.label && $scopedSlots[scopedSlots.label]) {
|
||||||
|
newLabel = $scopedSlots.label(list[i])
|
||||||
|
}
|
||||||
|
if (!newTitle && scopedSlots.title && $scopedSlots[scopedSlots.title]) {
|
||||||
|
newTitle = $scopedSlots.title(list[i])
|
||||||
|
}
|
||||||
|
const item = {
|
||||||
|
// label: newLabel,
|
||||||
|
title: newTitle || newLabel,
|
||||||
|
}
|
||||||
|
this.updateTreeData(children)
|
||||||
|
Object.assign(list[i], item)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderTreeSelect (locale) {
|
||||||
|
const props = getOptionProps(this)
|
||||||
|
const {
|
||||||
|
prefixCls,
|
||||||
|
size,
|
||||||
|
notFoundContent,
|
||||||
|
dropdownStyle,
|
||||||
|
dropdownClassName,
|
||||||
|
...restProps
|
||||||
|
} = props
|
||||||
|
this.updateTreeData(props.treeData)
|
||||||
|
const cls = {
|
||||||
|
[`${prefixCls}-lg`]: size === 'large',
|
||||||
|
[`${prefixCls}-sm`]: size === 'small',
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkable = getComponentFromProp(this, 'treeCheckable')
|
||||||
|
if (checkable) {
|
||||||
|
checkable = <span class={`${prefixCls}-tree-checkbox-inner`} />
|
||||||
|
}
|
||||||
|
const VcTreeSelectProps = {
|
||||||
|
props: {
|
||||||
|
...restProps,
|
||||||
|
dropdownClassName: classNames(dropdownClassName, `${prefixCls}-tree-dropdown`),
|
||||||
|
prefixCls,
|
||||||
|
dropdownStyle: { maxHeight: '100vh', overflow: 'auto', ...dropdownStyle },
|
||||||
|
treeCheckable: checkable,
|
||||||
|
notFoundContent: notFoundContent || locale.notFoundContent,
|
||||||
|
},
|
||||||
|
class: cls,
|
||||||
|
on: { ...this.$listeners, change: this.onChange },
|
||||||
|
ref: 'vcTreeSelect',
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<VcTreeSelect {...VcTreeSelectProps}>{filterEmpty(this.$slots.default)}</VcTreeSelect>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<LocaleReceiver
|
||||||
|
componentName='Select'
|
||||||
|
defaultLocale={{}}
|
||||||
|
scopedSlots={
|
||||||
|
{ default: this.renderTreeSelect }
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Tree props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| allowClear | 显示清除按钮 | boolean | false |
|
||||||
|
| defaultValue | 指定默认选中的条目 | string/string\[] | - |
|
||||||
|
| disabled | 是否禁用 | boolean | false |
|
||||||
|
| dropdownClassName | 下拉菜单的 className 属性 | string | - |
|
||||||
|
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | boolean | true |
|
||||||
|
| dropdownStyle | 下拉菜单的样式 | object | - |
|
||||||
|
| filterTreeNode | 是否根据输入项进行筛选,默认用 treeNodeFilterProp 的值作为要筛选的 TreeNode 的属性值 | boolean\|Function(inputValue: string, treeNode: TreeNode) (函数需要返回bool值) | Function |
|
||||||
|
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | () => document.body |
|
||||||
|
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 value 类型从 `string` 变为 `{value: string, label: VNode, halfChecked(treeCheckStrictly 时有效): string[] }` 的格式 | boolean | false |
|
||||||
|
| loadData | 异步加载数据 | function(node) | - |
|
||||||
|
| multiple | 支持多选(当设置 treeCheckable 时自动变为true) | boolean | false |
|
||||||
|
| placeholder | 选择框默认文字 | string\|slot | - |
|
||||||
|
| searchPlaceholder | 搜索框默认文字 | string\|slot | - |
|
||||||
|
| 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' |
|
||||||
|
| 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\[] | - |
|
||||||
|
| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' |
|
||||||
|
| treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' |
|
||||||
|
| value(v-model) | 指定当前选中的条目 | string/string\[] | - |
|
||||||
|
|
||||||
|
### 事件
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 回调参数 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| change | 选中树节点时调用此函数 | function(value, label, extra) | - |
|
||||||
|
| search | 文本框值变化时回调 | function(value: string) | - |
|
||||||
|
| select | 被选中时调用 | function(value, node, extra) | - |
|
||||||
|
|
||||||
|
### Tree 方法
|
||||||
|
|
||||||
|
| 名称 | 描述 |
|
||||||
|
| --- | --- |
|
||||||
|
| blur() | 移除焦点 |
|
||||||
|
| focus() | 获取焦点 |
|
||||||
|
|
||||||
|
### TreeNode props
|
||||||
|
|
||||||
|
> 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| disableCheckbox | 禁掉 checkbox | boolean | false |
|
||||||
|
| disabled | 是否禁用 | boolean | false |
|
||||||
|
| isLeaf | 是否是叶子节点 | boolean | false |
|
||||||
|
| key | 此项必须设置(其值在整个树范围内唯一) | string | - |
|
||||||
|
| title | 树节点显示的内容 | string\|slot | '---' |
|
||||||
|
| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - |
|
||||||
|
| scopedSlots | 使用treeData时,可以通过该属性配置支持slot的属性,如 `scopedSlots: { title: 'XXX'}` | object | - |
|
|
@ -0,0 +1,37 @@
|
||||||
|
import PropTypes from '../_util/vue-types'
|
||||||
|
import { AbstractSelectProps } from '../select'
|
||||||
|
|
||||||
|
export const TreeData = PropTypes.shape({
|
||||||
|
key: PropTypes.string,
|
||||||
|
value: PropTypes.string,
|
||||||
|
label: PropTypes.any,
|
||||||
|
scopedSlots: PropTypes.object,
|
||||||
|
children: PropTypes.array,
|
||||||
|
}).loose
|
||||||
|
|
||||||
|
export const TreeSelectProps = () => ({
|
||||||
|
...AbstractSelectProps(),
|
||||||
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.array]),
|
||||||
|
defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
// onSelect: (value: any) => void,
|
||||||
|
// 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,
|
||||||
|
treeCheckable: PropTypes.bool,
|
||||||
|
treeDefaultExpandedKeys: PropTypes.arrayOf(String),
|
||||||
|
filterTreeNode: PropTypes.func,
|
||||||
|
treeNodeFilterProp: PropTypes.string,
|
||||||
|
treeNodeLabelProp: PropTypes.string,
|
||||||
|
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,
|
||||||
|
})
|
|
@ -0,0 +1,6 @@
|
||||||
|
import '../../style/index.less'
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
// style dependencies
|
||||||
|
import '../../select/style'
|
||||||
|
import '../../checkbox/style'
|
|
@ -0,0 +1,145 @@
|
||||||
|
@import "../../style/themes/default";
|
||||||
|
@import "../../style/mixins/index";
|
||||||
|
@import "../../tree/style/mixin";
|
||||||
|
@import "../../checkbox/style/mixin";
|
||||||
|
|
||||||
|
@select-prefix-cls: ~"@{ant-prefix}-select";
|
||||||
|
@select-tree-prefix-cls: ~"@{ant-prefix}-select-tree";
|
||||||
|
|
||||||
|
.antCheckboxFn(@checkbox-prefix-cls: ~"@{ant-prefix}-select-tree-checkbox");
|
||||||
|
|
||||||
|
.@{select-tree-prefix-cls} {
|
||||||
|
.reset-component;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
margin-top: -4px;
|
||||||
|
li {
|
||||||
|
padding: 0;
|
||||||
|
margin: 8px 0;
|
||||||
|
list-style: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
outline: 0;
|
||||||
|
&.filter-node {
|
||||||
|
> span {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 18px;
|
||||||
|
}
|
||||||
|
.@{select-tree-prefix-cls}-node-content-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
color: @text-color;
|
||||||
|
transition: all .3s;
|
||||||
|
width: ~"calc(100% - 24px)";
|
||||||
|
&:hover {
|
||||||
|
background-color: @item-hover-bg;
|
||||||
|
}
|
||||||
|
&.@{select-tree-prefix-cls}-node-selected {
|
||||||
|
background-color: @primary-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
&.@{select-tree-prefix-cls}-checkbox {
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
+ .@{select-tree-prefix-cls}-node-content-wrapper {
|
||||||
|
width: ~"calc(100% - 46px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.@{select-tree-prefix-cls}-switcher,
|
||||||
|
&.@{select-tree-prefix-cls}-iconEle {
|
||||||
|
margin: 0;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 22px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
border: 0 none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
&.@{select-tree-prefix-cls}-icon_loading {
|
||||||
|
&:after {
|
||||||
|
display: inline-block;
|
||||||
|
.iconfont-font("\e6ae");
|
||||||
|
animation: loadingCircle 1s infinite linear;
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.@{select-tree-prefix-cls}-switcher {
|
||||||
|
&.@{select-tree-prefix-cls}-switcher-noop {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
&.@{select-tree-prefix-cls}-switcher_open {
|
||||||
|
.antTreeSwitcherIcon();
|
||||||
|
}
|
||||||
|
&.@{select-tree-prefix-cls}-switcher_close {
|
||||||
|
.antTreeSwitcherIcon();
|
||||||
|
&:after {
|
||||||
|
transform: rotate(270deg) scale(0.59);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-child-tree {
|
||||||
|
display: none;
|
||||||
|
&-open {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
li&-treenode-disabled {
|
||||||
|
> span:not(.@{select-tree-prefix-cls}-switcher),
|
||||||
|
> .@{select-tree-prefix-cls}-node-content-wrapper,
|
||||||
|
> .@{select-tree-prefix-cls}-node-content-wrapper span {
|
||||||
|
color: @disabled-color;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
> .@{select-tree-prefix-cls}-node-content-wrapper:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-icon__open {
|
||||||
|
margin-right: 2px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
&-icon__close {
|
||||||
|
margin-right: 2px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{select-prefix-cls}-tree-dropdown {
|
||||||
|
.reset-component;
|
||||||
|
.@{select-prefix-cls}-dropdown-search {
|
||||||
|
display: block;
|
||||||
|
padding: 4px;
|
||||||
|
.@{select-prefix-cls}-search__field__wrap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.@{select-prefix-cls}-search__field {
|
||||||
|
padding: 4px 7px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: @border-width-base @border-style-base @border-color-base;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
&.@{select-prefix-cls}-search--hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.@{select-prefix-cls}-not-found {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: @disabled-color;
|
||||||
|
padding: 7px 16px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,2 @@
|
||||||
|
@import "./select.less";
|
||||||
|
@import "./tree.less";
|
Binary file not shown.
After Width: | Height: | Size: 381 B |
Binary file not shown.
After Width: | Height: | Size: 45 B |
|
@ -0,0 +1,500 @@
|
||||||
|
@selectPrefixCls: rc-tree-select;
|
||||||
|
|
||||||
|
.effect() {
|
||||||
|
animation-duration: .3s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{selectPrefixCls} {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
&-allow-clear {
|
||||||
|
.@{selectPrefixCls}-selection--single .@{selectPrefixCls}-selection__rendered {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> ul > li > a {
|
||||||
|
padding: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow
|
||||||
|
&-arrow {
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px;
|
||||||
|
b {
|
||||||
|
border-color: #999999 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-selection {
|
||||||
|
outline: none;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
|
||||||
|
&__clear {
|
||||||
|
font-weight: bold;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '×'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enabled &-selection {
|
||||||
|
&:hover {
|
||||||
|
border-color: #23c0fa;
|
||||||
|
box-shadow: 0 0 2px fadeout(#2db7f5, 20%);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
border-color: #2db7f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-selection--single {
|
||||||
|
height: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-selection__rendered {
|
||||||
|
display: block;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 20px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-selection-selected-value {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-selection__clear {
|
||||||
|
top: 5px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-disabled {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-selection--single,
|
||||||
|
.@{selectPrefixCls}-selection__choice__remove {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: #ccc;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search__field__wrap {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search__field__placeholder {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 3px;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search__field__mirror {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -9999px;
|
||||||
|
white-space: pre;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search--inline {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
.@{selectPrefixCls}-search__field__wrap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.@{selectPrefixCls}-search__field {
|
||||||
|
border: none;
|
||||||
|
font-size: 100%;
|
||||||
|
//margin-top: 5px;
|
||||||
|
background: transparent;
|
||||||
|
outline: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
> i {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enabled&-selection--multiple {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-selection--multiple {
|
||||||
|
min-height: 28px;
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-search--inline {
|
||||||
|
width: auto;
|
||||||
|
.@{selectPrefixCls}-search__field {
|
||||||
|
width: 0.75em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-search__field__placeholder {
|
||||||
|
top: 5px;
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-selection__rendered {
|
||||||
|
//display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> ul > li {
|
||||||
|
margin-top: 4px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{selectPrefixCls}-selection__clear {
|
||||||
|
top: 5px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enabled {
|
||||||
|
.@{selectPrefixCls}-selection__choice {
|
||||||
|
cursor: default;
|
||||||
|
&:hover {
|
||||||
|
.@{selectPrefixCls}-selection__choice__remove {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
.@{selectPrefixCls}-selection__choice__remove +
|
||||||
|
.@{selectPrefixCls}-selection__choice__content {
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& &-selection__choice {
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
border-radius: 4px;
|
||||||
|
float: left;
|
||||||
|
padding: 0 15px;
|
||||||
|
margin-right: 4px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: padding .3s cubic-bezier(0.6, -0.28, 0.735, 0.045), width .3s cubic-bezier(0.6, -0.28, 0.735, 0.045);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
transition: margin .3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-zoom-enter, &-zoom-appear, &-zoom-leave {
|
||||||
|
.effect();
|
||||||
|
opacity: 0;
|
||||||
|
animation-play-state: paused;
|
||||||
|
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-zoom-leave {
|
||||||
|
opacity: 1;
|
||||||
|
animation-timing-function: cubic-bezier(0.6, -0.28, 0.735, 0.045);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-zoom-enter.@{selectPrefixCls}-selection__choice-zoom-enter-active,
|
||||||
|
&-zoom-appear.@{selectPrefixCls}-selection__choice-zoom-appear-active {
|
||||||
|
animation-play-state: running;
|
||||||
|
animation-name: rcSelectChoiceZoomIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-zoom-leave.@{selectPrefixCls}-selection__choice-zoom-leave-active {
|
||||||
|
animation-play-state: running;
|
||||||
|
animation-name: rcSelectChoiceZoomOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcSelectChoiceZoomIn {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.6);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcSelectChoiceZoomOut {
|
||||||
|
to {
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__remove {
|
||||||
|
color: #919191;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 0 0 8px;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0);
|
||||||
|
top: 0;
|
||||||
|
right: 2px;
|
||||||
|
transition: opacity .3s, transform .3s;
|
||||||
|
&:before {
|
||||||
|
content: '×'
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
box-shadow: 0 0px 4px #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 100;
|
||||||
|
left: -9999px;
|
||||||
|
top: -9999px;
|
||||||
|
//border-top: none;
|
||||||
|
//border-top-left-radius: 0;
|
||||||
|
//border-top-right-radius: 0;
|
||||||
|
position: absolute;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-menu {
|
||||||
|
outline: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
> li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item-group-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
> li.@{selectPrefixCls}-menu-item {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item-group-title {
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-bottom: 1px solid #dedede;
|
||||||
|
}
|
||||||
|
|
||||||
|
li&-item {
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
padding: 7px 10px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #666666;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&-selected {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
background-color: #5897fb;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-disabled {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-divider {
|
||||||
|
height: 1px;
|
||||||
|
margin: 1px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #e5e5e5;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slide-up-enter, &-slide-up-appear {
|
||||||
|
.effect();
|
||||||
|
opacity: 0;
|
||||||
|
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slide-up-leave {
|
||||||
|
.effect();
|
||||||
|
opacity: 1;
|
||||||
|
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft, &-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft {
|
||||||
|
animation-name: rcSelectDropdownSlideUpIn;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slide-up-leave&-slide-up-leave-active&-placement-bottomLeft {
|
||||||
|
animation-name: rcSelectDropdownSlideUpOut;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slide-up-enter&-slide-up-enter-active&-placement-topLeft, &-slide-up-appear&-slide-up-appear-active&-placement-topLeft {
|
||||||
|
animation-name: rcSelectDropdownSlideDownIn;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slide-up-leave&-slide-up-leave-active&-placement-topLeft {
|
||||||
|
animation-name: rcSelectDropdownSlideDownOut;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcSelectDropdownSlideUpIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: 0% 0%;
|
||||||
|
transform: scaleY(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform-origin: 0% 0%;
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes rcSelectDropdownSlideUpOut {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform-origin: 0% 0%;
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: 0% 0%;
|
||||||
|
transform: scaleY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcSelectDropdownSlideDownIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: 0% 100%;
|
||||||
|
transform: scaleY(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform-origin: 0% 100%;
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes rcSelectDropdownSlideDownOut {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform-origin: 0% 100%;
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: 0% 100%;
|
||||||
|
transform: scaleY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-dropdown-search {
|
||||||
|
display: block;
|
||||||
|
padding: 4px;
|
||||||
|
.@{selectPrefixCls}-search__field__wrap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.@{selectPrefixCls}-search__field__placeholder {
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
.@{selectPrefixCls}-search__field {
|
||||||
|
padding: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
&.@{selectPrefixCls}-search--hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-open {
|
||||||
|
.@{selectPrefixCls}-arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-not-found {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,347 @@
|
||||||
|
/* eslint react/no-multi-comp:0, no-console:0, no-alert: 0 */
|
||||||
|
|
||||||
|
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 { gData } from './util'
|
||||||
|
|
||||||
|
function isLeaf (value) {
|
||||||
|
if (!value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let queues = [...gData]
|
||||||
|
while (queues.length) { // BFS
|
||||||
|
const item = queues.shift()
|
||||||
|
if (item.value === value) {
|
||||||
|
if (!item.children) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
queues = queues.concat(item.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPath (value, data) {
|
||||||
|
const sel = []
|
||||||
|
function loop (selected, children) {
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
const item = children[i]
|
||||||
|
if (selected === item.value) {
|
||||||
|
sel.push(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
loop(selected, item.children, item)
|
||||||
|
if (sel.length) {
|
||||||
|
sel.push(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop(value, data)
|
||||||
|
return sel
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick () {
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose () {
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
onSearch (value) {
|
||||||
|
console.log(value, arguments)
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange (value) {
|
||||||
|
console.log('onChange', arguments)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeChildren (value) {
|
||||||
|
console.log('onChangeChildren', arguments)
|
||||||
|
const pre = value ? this.value : undefined
|
||||||
|
this.value = isLeaf(value) ? value : pre
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeLV (value) {
|
||||||
|
console.log('labelInValue', arguments)
|
||||||
|
if (!value) {
|
||||||
|
this.lv = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const path = findPath(value.value, gData).map(i => i.label).reverse().join(' > ')
|
||||||
|
this.lv = { value: value.value, label: path }
|
||||||
|
},
|
||||||
|
|
||||||
|
onMultipleChange (value) {
|
||||||
|
console.log('onMultipleChange', arguments)
|
||||||
|
this.multipleValue = value
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelect () {
|
||||||
|
// use onChange instead
|
||||||
|
console.log(...arguments)
|
||||||
|
},
|
||||||
|
|
||||||
|
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.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
filterTreeNode (input, child) {
|
||||||
|
return String(child.title).indexOf(input) === 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div style={{ margin: '20px' }}>
|
||||||
|
<h2>tree-select in dialog</h2>
|
||||||
|
<button class='btn btn-primary' onClick={this.onClick}>show dialog</button>
|
||||||
|
{this.visible ? <Dialog
|
||||||
|
visible={this.visible}
|
||||||
|
animation='zoom'
|
||||||
|
maskAnimation='fade'
|
||||||
|
onClose={this.onClose}
|
||||||
|
style={{ width: '600px', height: '400px', overflow: 'auto' }}
|
||||||
|
id='area'
|
||||||
|
>
|
||||||
|
<div style={{ height: '600px', paddingTop: '100px' }}>
|
||||||
|
<TreeSelect
|
||||||
|
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto', zIndex: 1500 }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
showSearch allowClear treeLine
|
||||||
|
value={this.value}
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='label'
|
||||||
|
filterTreeNode={false}
|
||||||
|
onSearch={this.onSearch}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog> : null}
|
||||||
|
|
||||||
|
<h2>single select</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
showSearch allowClear treeLine
|
||||||
|
inputValue={this.inputValue}
|
||||||
|
value={this.value}
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='label'
|
||||||
|
filterTreeNode={false}
|
||||||
|
onSearch={this.onSearch}
|
||||||
|
open={this.tsOpen}
|
||||||
|
onChange={(value) => {
|
||||||
|
console.log('onChange', value, arguments)
|
||||||
|
if (value === '0-0-0-0-value') {
|
||||||
|
this.tsOpen = true
|
||||||
|
} else {
|
||||||
|
this.tsOpen = false
|
||||||
|
}
|
||||||
|
this.value = value
|
||||||
|
} }
|
||||||
|
dropdownVisibleChange={(v, info) => {
|
||||||
|
console.log('single dropdownVisibleChange', v, info)
|
||||||
|
// document clicked
|
||||||
|
if (info.documentClickClose && this.value === '0-0-0-0-value') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} }
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>single select (just select children)</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
showSearch allowClear treeLine
|
||||||
|
value={this.value}
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='label'
|
||||||
|
filterTreeNode={false}
|
||||||
|
onChange={this.onChangeChildren}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>multiple select</h2>
|
||||||
|
<TreeSelect ref='mul'
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
multiple
|
||||||
|
value={this.multipleValue}
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
onChange={this.onMultipleChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>check select</h2>
|
||||||
|
<TreeSelect
|
||||||
|
class='check-select'
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ height: '200px', overflow: 'auto' }}
|
||||||
|
dropdownPopupAlign={{ overflow: { adjustY: 0, adjustX: 0 }, offset: [0, 2] }}
|
||||||
|
onDropdownVisibleChange={this.onDropdownVisibleChange}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
treeLine maxTagTextLength={10}
|
||||||
|
value={this.value}
|
||||||
|
inputValue={null}
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>labelInValue & show path</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '500px' }}
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
showSearch allowClear treeLine
|
||||||
|
value={this.lv} labelInValue
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='label'
|
||||||
|
filterTreeNode={false}
|
||||||
|
onChange={this.onChangeLV}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>use treeDataSimpleMode</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
treeLine maxTagTextLength={10}
|
||||||
|
inputValue={'test111'}
|
||||||
|
value={this.value}
|
||||||
|
treeData={this.simpleTreeData}
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
treeDataSimpleMode={this.treeDataSimpleMode}
|
||||||
|
treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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}
|
||||||
|
treeDefaultExpandAll
|
||||||
|
treeData={[
|
||||||
|
{ key: '', value: '', label: 'empty value', children: [] },
|
||||||
|
{
|
||||||
|
key: '0', value: '0', label: '0 label', children: [
|
||||||
|
{ key: '00', value: '00', label: '00 label', children: [] },
|
||||||
|
{ key: '01', value: '01', label: '01 label', children: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onChange={(val) => console.log(val, arguments)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>use TreeNode Component (not recommend)</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
defaultValue={'leaf1'}
|
||||||
|
treeDefaultExpandAll
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
filterTreeNode={this.filterTreeNode}
|
||||||
|
onChange={(val) => console.log(val, arguments)}
|
||||||
|
>
|
||||||
|
<TreeNode value='' title='parent 1' key=''>
|
||||||
|
<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' />
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { generateData, calcTotal } from './util'
|
||||||
|
|
||||||
|
const Gen = {
|
||||||
|
props: {
|
||||||
|
x: PropTypes.number.def(20),
|
||||||
|
y: PropTypes.number.def(18),
|
||||||
|
z: PropTypes.number.def(1),
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
nums: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
this.$refs.x.value = this.x
|
||||||
|
this.$refs.y.value = this.y
|
||||||
|
this.$refs.z.value = this.z
|
||||||
|
const vals = this.getVals()
|
||||||
|
this.$emit('gen', generateData(vals.x, vals.y, vals.z))
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onGen (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
const vals = this.getVals()
|
||||||
|
this.$emit('gen', generateData(vals.x, vals.y, vals.z))
|
||||||
|
this.nums = calcTotal(vals.x, vals.y, vals.z)
|
||||||
|
},
|
||||||
|
getVals () {
|
||||||
|
return {
|
||||||
|
x: parseInt(this.$refs.x.value, 10),
|
||||||
|
y: parseInt(this.$refs.y.value, 10),
|
||||||
|
z: parseInt(this.$refs.z.value, 10),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { x, y, z } = this
|
||||||
|
return (<div style={{ padding: '0 20px' }}>
|
||||||
|
<h2>big data generator</h2>
|
||||||
|
<form onSubmit={this.onGen}>
|
||||||
|
<span style={{ marginRight: '10px' }}>
|
||||||
|
x: <input ref='x' type='number' min='1' required style={{ width: '50px' }} />
|
||||||
|
</span>
|
||||||
|
<span style={{ marginRight: '10px' }}>
|
||||||
|
y: <input ref='y' type='number' min='1' required style={{ width: '50px' }} />
|
||||||
|
</span>
|
||||||
|
<span style={{ marginRight: '10px' }}>
|
||||||
|
z: <input ref='z' type='number' min='1' required style={{ width: '50px' }} />
|
||||||
|
</span>
|
||||||
|
<button type='submit'>Generate</button>
|
||||||
|
<p>total nodes: {this.nums || calcTotal(x, y, z)}</p>
|
||||||
|
</form>
|
||||||
|
<p style={{ fontSize: '12px' }}>
|
||||||
|
x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级)
|
||||||
|
</p>
|
||||||
|
</div>)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Gen
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* eslint react/no-multi-comp:0, no-console:0 */
|
||||||
|
|
||||||
|
import '../assets/index.less'
|
||||||
|
import './demo.less'
|
||||||
|
import TreeSelect, { SHOW_PARENT } from '../index'
|
||||||
|
import Gen from './big-data-generator'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
gData: [],
|
||||||
|
gData1: [],
|
||||||
|
value: '',
|
||||||
|
value1: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange (value) {
|
||||||
|
console.log('onChange', arguments)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeStrictly (value1) {
|
||||||
|
console.log('onChangeStrictly', arguments)
|
||||||
|
const ind = parseInt(Math.random() * 3, 10)
|
||||||
|
value1.push({ value: `0-0-0-${ind}-value`, label: `0-0-0-${ind}-label`, halfChecked: true })
|
||||||
|
this.value1 = value1
|
||||||
|
},
|
||||||
|
|
||||||
|
onGen (data) {
|
||||||
|
Object.assign(this.$data, {
|
||||||
|
gData: data,
|
||||||
|
gData1: [...data],
|
||||||
|
value: '0-0-0-value',
|
||||||
|
value1: [
|
||||||
|
{ value: '0-0-value', label: '0-0-label', halfChecked: true },
|
||||||
|
{ value: '0-0-0-value', label: '0-0-0-label' },
|
||||||
|
],
|
||||||
|
// value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (<div style={{ padding: '0 20px' }}>
|
||||||
|
<Gen onGen={this.onGen} />
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<div style={{ marginRight: '20px' }}>
|
||||||
|
<h3>normal check</h3>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
treeData={this.gData} treeLine
|
||||||
|
value={this.value}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
treeCheckable
|
||||||
|
showCheckedStrategy={SHOW_PARENT}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>checkStrictly</h3>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
treeData={this.gData1} treeLine
|
||||||
|
value={this.value1}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
treeCheckable
|
||||||
|
treeCheckStrictly
|
||||||
|
showCheckedStrategy={SHOW_PARENT}
|
||||||
|
onChange={this.onChangeStrictly}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
.rc-tree-select-selection--multiple {
|
||||||
|
max-height: 50px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.rc-tree-select-dropdown {
|
||||||
|
max-height: 350px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.check-select {
|
||||||
|
width: 300px;
|
||||||
|
.rc-tree-select-selection--multiple {
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/* eslint react/no-multi-comp:0, no-console:0 */
|
||||||
|
import '../assets/index.less'
|
||||||
|
import TreeSelect from '../index'
|
||||||
|
|
||||||
|
const SHOW_PARENT = TreeSelect.SHOW_PARENT
|
||||||
|
|
||||||
|
const treeData = [{
|
||||||
|
label: 'Node1',
|
||||||
|
value: '0-0',
|
||||||
|
key: '0-0',
|
||||||
|
children: [{
|
||||||
|
label: 'Child Node1',
|
||||||
|
value: '0-0-0',
|
||||||
|
key: '0-0-0',
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
label: 'Node2',
|
||||||
|
value: '0-1',
|
||||||
|
key: '0-1',
|
||||||
|
children: [{
|
||||||
|
label: 'Child Node3',
|
||||||
|
value: '0-1-0',
|
||||||
|
key: '0-1-0',
|
||||||
|
}, {
|
||||||
|
label: 'Child Node4',
|
||||||
|
value: '0-1-1',
|
||||||
|
key: '0-1-1',
|
||||||
|
}, {
|
||||||
|
label: 'Child Node5',
|
||||||
|
value: '0-1-2',
|
||||||
|
key: '0-1-2',
|
||||||
|
}],
|
||||||
|
}]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
value: ['0-0-0'],
|
||||||
|
disabled: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onChange (value) {
|
||||||
|
console.log('onChange ', value, arguments)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
switch (checked) {
|
||||||
|
this.disabled = checked
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const tProps = {
|
||||||
|
props: {
|
||||||
|
treeData,
|
||||||
|
disabled: this.disabled,
|
||||||
|
value: this.value,
|
||||||
|
multiple: true,
|
||||||
|
allowClear: true,
|
||||||
|
treeCheckable: true,
|
||||||
|
showCheckedStrategy: SHOW_PARENT,
|
||||||
|
searchPlaceholder: 'Please select',
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
change: this.onChange,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
width: '300px',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TreeSelect {...tProps} />
|
||||||
|
<input type='checkbox' onChange={e => this.switch(e.target.checked)}/> 禁用
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* eslint react/no-multi-comp:0, no-console:0 */
|
||||||
|
|
||||||
|
import '../assets/index.less'
|
||||||
|
import TreeSelect from '../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' },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onChange (value) {
|
||||||
|
console.log(value)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadData (treeNode) {
|
||||||
|
console.log(treeNode)
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const treeData = [...this.treeData]
|
||||||
|
getNewTreeData(treeData, treeNode.eventKey, generateTreeNodes(treeNode), 2)
|
||||||
|
this.treeData = treeData
|
||||||
|
resolve()
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '10px 30px' }}>
|
||||||
|
<h2>dynamic render</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
treeData={this.treeData}
|
||||||
|
labelInValue
|
||||||
|
value={this.value}
|
||||||
|
onChange={this.onChange}
|
||||||
|
loadData={this.onLoadData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* eslint react/no-multi-comp:0, no-console:0 */
|
||||||
|
|
||||||
|
import '../assets/index.less'
|
||||||
|
import TreeSelect, { SHOW_PARENT } from '../index'
|
||||||
|
import { gData } from './util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
value: '11',
|
||||||
|
// value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
|
||||||
|
simpleTreeData: [
|
||||||
|
{ key: 1, pId: 0, label: 'a', value: 'a' },
|
||||||
|
{ key: 11, pId: 1, label: 'a12', value: 'a12', disabled: true },
|
||||||
|
{ key: 111, pId: 11, label: 'a00', value: 'a00', selectable: false },
|
||||||
|
{ key: 2, pId: 0, label: 'b', value: 'b' },
|
||||||
|
{ key: 20, pId: 2, label: 'b10', value: 'b10' },
|
||||||
|
{ key: 21, pId: 2, label: 'b1', value: 'b1' },
|
||||||
|
{ key: 22, pId: 2, label: 'b12', value: 'b12' },
|
||||||
|
],
|
||||||
|
treeDataSimpleMode: {
|
||||||
|
id: 'key',
|
||||||
|
rootPId: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange (value) {
|
||||||
|
if (value.length === 1) {
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
console.log('onChange', arguments, this.simpleTreeData)
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelect () {
|
||||||
|
// use onChange instead
|
||||||
|
// console.log(arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDataChange () {
|
||||||
|
const data = [...this.simpleTreeData]
|
||||||
|
data.forEach(i => {
|
||||||
|
if (i.key === 11) {
|
||||||
|
delete i.disabled
|
||||||
|
}
|
||||||
|
if (i.key === 20) {
|
||||||
|
i.disabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.simpleTreeData = data
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div style={{ margin: '20px' }}>
|
||||||
|
<h2>check select</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
transitionName='rc-tree-select-dropdown-slide-up'
|
||||||
|
choiceTransitionName='rc-tree-select-selection__choice-zoom'
|
||||||
|
dropdownStyle={{ height: '200px', overflow: 'auto' }}
|
||||||
|
dropdownPopupAlign={{ overflow: { adjustY: 0, adjustX: 0 }, offset: [0, 2] }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
treeLine maxTagTextLength={10}
|
||||||
|
value={this.value}
|
||||||
|
treeData={gData}
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
treeCheckable
|
||||||
|
onChange={this.onChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>use treeDataSimpleMode</h2>
|
||||||
|
<TreeSelect
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
dropdownStyle={{ maxHeight: '200px', overflow: 'auto' }}
|
||||||
|
placeholder={<i>请下拉选择</i>}
|
||||||
|
searchPlaceholder='please search'
|
||||||
|
treeLine maxTagTextLength={10}
|
||||||
|
inputValue={null}
|
||||||
|
value={this.value}
|
||||||
|
treeData={this.simpleTreeData}
|
||||||
|
treeDefaultExpandAll
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
treeDataSimpleMode={this.treeDataSimpleMode}
|
||||||
|
treeCheckable showCheckedStrategy={SHOW_PARENT}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
/>
|
||||||
|
<button onClick={this.onDataChange}>change data</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
import '../assets/index.less'
|
||||||
|
import TreeSelect from '../index'
|
||||||
|
import Select from '../../vc-select'
|
||||||
|
import { createForm } from '../../vc-form'
|
||||||
|
import { regionStyle, errorStyle } from './styles'
|
||||||
|
import { gData } from './util'
|
||||||
|
import '../../vc-select/assets/index.less'
|
||||||
|
import './demo.less'
|
||||||
|
|
||||||
|
const { Option } = Select
|
||||||
|
|
||||||
|
const TreeSelectInput = {
|
||||||
|
props: [
|
||||||
|
'multiple',
|
||||||
|
'treeData',
|
||||||
|
'treeCheckable',
|
||||||
|
'value',
|
||||||
|
],
|
||||||
|
methods: {
|
||||||
|
onChange (value) {
|
||||||
|
console.log(value, arguments)
|
||||||
|
this.$emit('change', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<TreeSelect {...{ props: this.$props }} onChange={this.onChange.bind(this)} />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Form = {
|
||||||
|
methods: {
|
||||||
|
onSubmit (e) {
|
||||||
|
console.log('submit')
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((error, values) => {
|
||||||
|
if (!error) {
|
||||||
|
console.log('ok', values)
|
||||||
|
} else {
|
||||||
|
console.log('error', error, values)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reset (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.resetFields()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { form } = this
|
||||||
|
const { getFieldDecorator, getFieldError } = form
|
||||||
|
const tProps = {
|
||||||
|
props: {
|
||||||
|
multiple: true,
|
||||||
|
treeData: gData,
|
||||||
|
treeCheckable: true,
|
||||||
|
// treeDefaultExpandAll: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (<div style={{ margin: '20px' }}>
|
||||||
|
<h2>validity</h2>
|
||||||
|
<form onSubmit={this.onSubmit}>
|
||||||
|
<div style={regionStyle}>
|
||||||
|
<div>
|
||||||
|
<p style={{ color: 'blue' }}>no onChange</p>
|
||||||
|
{getFieldDecorator('tree-select', {
|
||||||
|
initialValue: ['0-0-0-value'],
|
||||||
|
rules: [
|
||||||
|
{ required: true, type: 'array', message: 'tree-select 需要必填' },
|
||||||
|
],
|
||||||
|
})(
|
||||||
|
<TreeSelect
|
||||||
|
{...tProps}
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p style={errorStyle}>
|
||||||
|
{(getFieldError('tree-select')) ? getFieldError('tree-select').join(',') : null}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={regionStyle}>
|
||||||
|
<div>
|
||||||
|
<p style={{ color: 'blue' }}>custom onChange</p>
|
||||||
|
{getFieldDecorator('tree-select1', {
|
||||||
|
initialValue: ['0-0-0-value'],
|
||||||
|
rules: [
|
||||||
|
{ required: true, type: 'array', message: 'tree-select1 需要必填' },
|
||||||
|
],
|
||||||
|
})(
|
||||||
|
<TreeSelectInput
|
||||||
|
{...tProps}
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
// treeData={gData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p style={errorStyle}>
|
||||||
|
{(getFieldError('tree-select1')) ? getFieldError('tree-select1').join(',') : null}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={regionStyle}>
|
||||||
|
{getFieldDecorator('select', {
|
||||||
|
initialValue: ['jack'],
|
||||||
|
rules: [
|
||||||
|
{ required: true, type: 'array', message: 'select 需要必填' },
|
||||||
|
],
|
||||||
|
})(
|
||||||
|
<Select
|
||||||
|
style={{ width: '200px' }} allowClear multiple
|
||||||
|
>
|
||||||
|
<Option value='jack'>jack</Option>
|
||||||
|
<Option value='lucy'>lucy</Option>
|
||||||
|
<Option value='disabled' disabled>disabled</Option>
|
||||||
|
<Option value='yiminghe'>yiminghe</Option>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
<p style={errorStyle}>
|
||||||
|
{(getFieldError('select')) ? getFieldError('select').join(',') : null}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={regionStyle}>
|
||||||
|
<button onClick={this.reset}>reset</button>
|
||||||
|
|
||||||
|
<input type='submit' value='submit'/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createForm()(Form)
|
|
@ -0,0 +1,11 @@
|
||||||
|
export const regionStyle = {
|
||||||
|
border: '1px solid red',
|
||||||
|
marginTop: '10px',
|
||||||
|
padding: '10px',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const errorStyle = {
|
||||||
|
color: 'red',
|
||||||
|
marginTop: '10px',
|
||||||
|
padding: '10px',
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
/* eslint no-loop-func: 0, no-console: 0 */
|
||||||
|
|
||||||
|
export function generateData (x = 3, y = 2, z = 1, gData = []) {
|
||||||
|
// x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级)
|
||||||
|
function _loop (_level, _preKey, _tns) {
|
||||||
|
const preKey = _preKey || '0'
|
||||||
|
const tns = _tns || gData
|
||||||
|
|
||||||
|
const children = []
|
||||||
|
for (let i = 0; i < x; i++) {
|
||||||
|
const key = `${preKey}-${i}`
|
||||||
|
tns.push({
|
||||||
|
label: `${key}-label`,
|
||||||
|
value: `${key}-value`,
|
||||||
|
key,
|
||||||
|
disabled: key === '0-0-0-1' || false,
|
||||||
|
})
|
||||||
|
if (i < y) {
|
||||||
|
children.push(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_level < 0) {
|
||||||
|
return tns
|
||||||
|
}
|
||||||
|
const __level = _level - 1
|
||||||
|
children.forEach((key, index) => {
|
||||||
|
tns[index].children = []
|
||||||
|
return _loop(__level, key, tns[index].children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_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
|
||||||
|
return rec(z + 1)
|
||||||
|
}
|
||||||
|
console.log('总节点数(单个tree):', calcTotal())
|
||||||
|
export const gData = generateData()
|
||||||
|
|
||||||
|
export function generateTreeNodes (treeNode) {
|
||||||
|
const arr = []
|
||||||
|
const key = treeNode.eventKey
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
arr.push({ label: `${key}-${i}-label`, value: `${key}-${i}-value`, key: `${key}-${i}` })
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLeaf (treeData, curKey, level) {
|
||||||
|
const loopLeaf = (data, lev) => {
|
||||||
|
const l = lev - 1
|
||||||
|
data.forEach((item) => {
|
||||||
|
if ((item.key.length > curKey.length) ? item.key.indexOf(curKey) !== 0
|
||||||
|
: curKey.indexOf(item.key) !== 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
loopLeaf(item.children, l)
|
||||||
|
} else if (l < 1) {
|
||||||
|
item.isLeaf = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loopLeaf(treeData, level + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNewTreeData (treeData, curKey, child, level) {
|
||||||
|
const loop = (data) => {
|
||||||
|
if (level < 1 || curKey.length - 3 > level * 2) return
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (curKey.indexOf(item.key) === 0) {
|
||||||
|
if (item.children) {
|
||||||
|
loop(item.children)
|
||||||
|
} else {
|
||||||
|
item.children = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loop(treeData)
|
||||||
|
setLeaf(treeData, curKey, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loopData (data, callback) {
|
||||||
|
const loop = (d, level = 0) => {
|
||||||
|
d.forEach((item, index) => {
|
||||||
|
const pos = `${level}-${index}`
|
||||||
|
if (item.children) {
|
||||||
|
loop(item.children, pos)
|
||||||
|
}
|
||||||
|
callback(item, index, pos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loop(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPositionPrefix (smallPos, bigPos) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// console.log(isPositionPrefix("0-1", "0-10-1"));
|
||||||
|
|
||||||
|
export function getFilterValue (val, sVal, delVal) {
|
||||||
|
const allPos = []
|
||||||
|
const delPos = []
|
||||||
|
loopData(gData, (item, index, pos) => {
|
||||||
|
if (sVal.indexOf(item.value) > -1) {
|
||||||
|
allPos.push(pos)
|
||||||
|
}
|
||||||
|
if (delVal.indexOf(item.value) > -1) {
|
||||||
|
delPos.push(pos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const newPos = []
|
||||||
|
delPos.forEach((item) => {
|
||||||
|
allPos.forEach((i) => {
|
||||||
|
if (isPositionPrefix(item, i) || isPositionPrefix(i, item)) {
|
||||||
|
// 过滤掉 父级节点 和 所有子节点。
|
||||||
|
// 因为 node节点 不选时,其 父级节点 和 所有子节点 都不选。
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newPos.push(i)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const newVal = []
|
||||||
|
if (newPos.length) {
|
||||||
|
loopData(gData, (item, index, pos) => {
|
||||||
|
if (newPos.indexOf(pos) > -1) {
|
||||||
|
newVal.push(item.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return newVal
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// rc-tree-select 1.12.13 tag
|
||||||
|
// export this package's api
|
||||||
|
import TreeSelect from './src'
|
||||||
|
|
||||||
|
export default TreeSelect
|
||||||
|
|
||||||
|
export { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './src'
|
|
@ -0,0 +1,108 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './strategies'
|
||||||
|
|
||||||
|
function nonEmptyStringType (props, propsName) {
|
||||||
|
const value = props[propsName]
|
||||||
|
if (typeof value !== 'string' || !value) {
|
||||||
|
return new Error() // Just a flag, so don't need message.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueType (props, propName, componentName) {
|
||||||
|
const labelInValueShape = PropTypes.shape({
|
||||||
|
value: nonEmptyStringType,
|
||||||
|
label: PropTypes.node,
|
||||||
|
})
|
||||||
|
if (props.labelInValue) {
|
||||||
|
const validate = PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(labelInValueShape),
|
||||||
|
labelInValueShape,
|
||||||
|
])
|
||||||
|
const error = validate(...arguments)
|
||||||
|
if (error) {
|
||||||
|
return new Error(
|
||||||
|
`Invalid prop \`${propName}\` supplied to \`${componentName}\`, ` +
|
||||||
|
`when \`labelInValue\` is \`true\`, \`${propName}\` should in ` +
|
||||||
|
`shape of \`{ value: string, label?: string }\`.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (props.treeCheckable && props.treeCheckStrictly) {
|
||||||
|
const validate = PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(labelInValueShape),
|
||||||
|
labelInValueShape,
|
||||||
|
])
|
||||||
|
const error = validate(...arguments)
|
||||||
|
if (error) {
|
||||||
|
return new Error(
|
||||||
|
`Invalid prop \`${propName}\` supplied to \`${componentName}\`, ` +
|
||||||
|
`when \`treeCheckable\` and \`treeCheckStrictly\` are \`true\`, ` +
|
||||||
|
`\`${propName}\` should in shape of \`{ value: string, label?: string }\`.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (props.multiple && props[propName] === '') {
|
||||||
|
return new Error(
|
||||||
|
`Invalid prop \`${propName}\` of type \`string\` supplied to \`${componentName}\`, ` +
|
||||||
|
`expected \`array\` when \`multiple\` is \`true\`.`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const validate = PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(PropTypes.string),
|
||||||
|
PropTypes.string,
|
||||||
|
])
|
||||||
|
return validate(...arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,371 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import omit from 'omit.js'
|
||||||
|
import Trigger from '../../trigger'
|
||||||
|
import Tree, { TreeNode } from '../../vc-tree'
|
||||||
|
import { SelectPropTypes } from './PropTypes'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import {
|
||||||
|
loopAllChildren,
|
||||||
|
flatToHierarchy,
|
||||||
|
getValuePropValue,
|
||||||
|
labelCompatible,
|
||||||
|
saveRef,
|
||||||
|
} from './util'
|
||||||
|
|
||||||
|
import { cloneElement } from '../../_util/vnode'
|
||||||
|
import { isEmptyElement, getSlotOptions, getKey, getAllProps, getComponentFromProp } from '../../_util/props-util'
|
||||||
|
import { noop } from '../../_util/vue-types/utils'
|
||||||
|
|
||||||
|
const BUILT_IN_PLACEMENTS = {
|
||||||
|
bottomLeft: {
|
||||||
|
points: ['tl', 'bl'],
|
||||||
|
offset: [0, 4],
|
||||||
|
overflow: {
|
||||||
|
adjustX: 0,
|
||||||
|
adjustY: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topLeft: {
|
||||||
|
points: ['bl', 'tl'],
|
||||||
|
offset: [0, -4],
|
||||||
|
overflow: {
|
||||||
|
adjustX: 0,
|
||||||
|
adjustY: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.setDropdownWidth()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
inputValue (val) {
|
||||||
|
// set autoExpandParent to true
|
||||||
|
this.setState({
|
||||||
|
sExpandedKeys: [],
|
||||||
|
fireOnExpand: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
updated () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.setDropdownWidth()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
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}`
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (props.inputValue) {
|
||||||
|
treeNodes = this.processTreeNode(treeNodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
popupPlacement='bottomLeft'
|
||||||
|
builtinPlacements={BUILT_IN_PLACEMENTS}
|
||||||
|
popupAlign={props.dropdownPopupAlign}
|
||||||
|
prefixCls={dropdownPrefixCls}
|
||||||
|
popupTransitionName={this.getDropdownTransitionName()}
|
||||||
|
onPopupVisibleChange={props.dropdownVisibleChange}
|
||||||
|
popup={popupElement}
|
||||||
|
popupVisible={visible}
|
||||||
|
getPopupContainer={props.getPopupContainer}
|
||||||
|
popupClassName={classnames(popupClassName)}
|
||||||
|
popupStyle={popupStyle}
|
||||||
|
>
|
||||||
|
{this.$slots.default}
|
||||||
|
</Trigger>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectTrigger
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TreeNode } from '../../vc-tree'
|
||||||
|
export default {
|
||||||
|
name: 'TreeNode',
|
||||||
|
__ANT_TREE_SELECT_NODE: true,
|
||||||
|
props: {
|
||||||
|
...TreeNode.props,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
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 }
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const SHOW_ALL = 'SHOW_ALL'
|
||||||
|
export const SHOW_PARENT = 'SHOW_PARENT'
|
||||||
|
export const SHOW_CHILD = 'SHOW_CHILD'
|
|
@ -0,0 +1,569 @@
|
||||||
|
import { getPropsData, getAllProps, getKey, getAttrs, getSlotOptions, filterEmpty, getSlots } from '../../_util/props-util'
|
||||||
|
import { cloneVNodes, cloneElement } from '../../_util/vnode'
|
||||||
|
export function toTitle (title) {
|
||||||
|
if (typeof title === 'string') {
|
||||||
|
return 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 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 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UNSELECTABLE_STYLE = {
|
||||||
|
userSelect: 'none',
|
||||||
|
WebkitUserSelect: 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UNSELECTABLE_ATTRIBUTE = {
|
||||||
|
unselectable: 'unselectable',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function labelCompatible (prop) {
|
||||||
|
let newProp = prop
|
||||||
|
if (newProp === 'label') {
|
||||||
|
newProp = 'title'
|
||||||
|
}
|
||||||
|
return newProp
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isInclude (smallArray, bigArray) {
|
||||||
|
// attention: [0,0,1] [0,0,10]
|
||||||
|
return smallArray.every((ii, i) => {
|
||||||
|
return ii === bigArray[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getChildrenlength (children) {
|
||||||
|
let len = 1
|
||||||
|
if (Array.isArray(children)) {
|
||||||
|
len = children.length
|
||||||
|
}
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return newChilds
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loop(filterChild(childs), 0, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
const hierarchyNodes = []
|
||||||
|
const levelObj = {}
|
||||||
|
arr.forEach((item) => {
|
||||||
|
if (!item.pos) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// console.log(Date.now()-s, objKeys.length, checkIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
halfCheckedKeys, checkedKeys, checkedNodes, treeNodesStates, checkedPositions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
handleCheckState(treeNodesStates, filterParentPosition(checkedPositions.sort()), true)
|
||||||
|
|
||||||
|
return getCheck(treeNodesStates, checkedPositions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function recursive (children, cb) {
|
||||||
|
children.forEach(item => {
|
||||||
|
cb(item)
|
||||||
|
if (item.children) {
|
||||||
|
recursive(item.children, cb)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
}
|
|
@ -558,7 +558,7 @@ const Tree = {
|
||||||
tabIndex={focusable ? '0' : null}
|
tabIndex={focusable ? '0' : null}
|
||||||
onKeydown={focusable ? this.onKeydown : () => {}}
|
onKeydown={focusable ? this.onKeydown : () => {}}
|
||||||
>
|
>
|
||||||
{children.map(this.renderTreeNode)}
|
{children.map((child, index) => this.renderTreeNode(child, index))}
|
||||||
</ul>
|
</ul>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,6 +21,7 @@ let onlyTreeNodeWarned = false // Only accept TreeNode
|
||||||
const TreeNode = {
|
const TreeNode = {
|
||||||
name: 'TreeNode',
|
name: 'TreeNode',
|
||||||
mixins: [BaseMixin],
|
mixins: [BaseMixin],
|
||||||
|
__ANT_TREE_NODE: true,
|
||||||
props: initDefaultProps({
|
props: initDefaultProps({
|
||||||
eventKey: PropTypes.string, // Pass by parent `cloneElement`
|
eventKey: PropTypes.string, // Pass by parent `cloneElement`
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: PropTypes.string,
|
||||||
|
|
|
@ -332,6 +332,11 @@ export function calcCheckStateConduct (treeNodes, checkedKeys) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// function keyListToString (keyList) {
|
||||||
|
// if (!keyList) return keyList
|
||||||
|
// return keyList.map(key => String(key))
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the value of checked and halfChecked keys.
|
* Calculate the value of checked and halfChecked keys.
|
||||||
* This should be only run in init or props changed.
|
* This should be only run in init or props changed.
|
||||||
|
@ -361,6 +366,9 @@ export function calcCheckedKeys (keys, props, children = []) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keyProps.checkedKeys = keyListToString(keyProps.checkedKeys)
|
||||||
|
// keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys)
|
||||||
|
|
||||||
// Do nothing if is checkStrictly mode
|
// Do nothing if is checkStrictly mode
|
||||||
if (checkStrictly) {
|
if (checkStrictly) {
|
||||||
return keyProps
|
return keyProps
|
||||||
|
|
|
@ -45,7 +45,7 @@ import {
|
||||||
Table,
|
Table,
|
||||||
Transfer,
|
Transfer,
|
||||||
Tree,
|
Tree,
|
||||||
// TreeSelect,
|
TreeSelect,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tag,
|
Tag,
|
||||||
TimePicker,
|
TimePicker,
|
||||||
|
@ -133,7 +133,8 @@ Vue.component(Table.ColumnGroup.name, Table.ColumnGroup)
|
||||||
Vue.component(Transfer.name, Transfer)
|
Vue.component(Transfer.name, Transfer)
|
||||||
Vue.component(Tree.name, Tree)
|
Vue.component(Tree.name, Tree)
|
||||||
Vue.component(Tree.TreeNode.name, Tree.TreeNode)
|
Vue.component(Tree.TreeNode.name, Tree.TreeNode)
|
||||||
// Vue.component(TreeSelect.name, TreeSelect)
|
Vue.component(TreeSelect.name, TreeSelect)
|
||||||
|
Vue.component(TreeSelect.TreeNode.name, TreeSelect.TreeNode)
|
||||||
Vue.component(Tabs.name, Tabs)
|
Vue.component(Tabs.name, Tabs)
|
||||||
Vue.component(Tabs.TabPane.name, Tabs.TabPane)
|
Vue.component(Tabs.TabPane.name, Tabs.TabPane)
|
||||||
Vue.component(Tag.name, Tag)
|
Vue.component(Tag.name, Tag)
|
||||||
|
|
|
@ -43,6 +43,7 @@ export { default as inputNumber } from 'antd/input-number/demo/index.vue'
|
||||||
export { default as transfer } from 'antd/transfer/demo/index.vue'
|
export { default as transfer } from 'antd/transfer/demo/index.vue'
|
||||||
export { default as upload } from 'antd/upload/demo/index.vue'
|
export { default as upload } from 'antd/upload/demo/index.vue'
|
||||||
export { default as tree } from 'antd/tree/demo/index.vue'
|
export { default as tree } from 'antd/tree/demo/index.vue'
|
||||||
|
export { default as treeSelect } from 'antd/tree-select/demo/index.vue'
|
||||||
export { default as layout } from 'antd/layout/demo/index.vue'
|
export { default as layout } from 'antd/layout/demo/index.vue'
|
||||||
export { default as form } from 'antd/form/demo/index.vue'
|
export { default as form } from 'antd/form/demo/index.vue'
|
||||||
export { default as anchor } from 'antd/anchor/demo/index.vue'
|
export { default as anchor } from 'antd/anchor/demo/index.vue'
|
||||||
|
|
|
@ -48,6 +48,7 @@ Array [
|
||||||
"Table",
|
"Table",
|
||||||
"Transfer",
|
"Transfer",
|
||||||
"Tree",
|
"Tree",
|
||||||
|
"TreeSelect",
|
||||||
"Tabs",
|
"Tabs",
|
||||||
"Tag",
|
"Tag",
|
||||||
"TimePicker",
|
"TimePicker",
|
||||||
|
|
Loading…
Reference in New Issue