mirror of
https://gitee.com/ElemeFE/element.git
synced 2024-12-02 12:18:46 +08:00
Select: add allow-create doc and test
This commit is contained in:
parent
1942e63453
commit
2d07391d3c
@ -62,6 +62,16 @@
|
||||
}]
|
||||
}],
|
||||
options4: [],
|
||||
options5: [{
|
||||
value: 'HTML',
|
||||
label: 'HTML'
|
||||
}, {
|
||||
value: 'CSS',
|
||||
label: 'CSS'
|
||||
}, {
|
||||
value: 'JavaScript',
|
||||
label: 'JavaScript'
|
||||
}],
|
||||
cities: [{
|
||||
value: 'Beijing',
|
||||
label: 'Beijing'
|
||||
@ -87,9 +97,10 @@
|
||||
value4: '',
|
||||
value5: [],
|
||||
value6: '',
|
||||
value7: [],
|
||||
value7: '',
|
||||
value8: '',
|
||||
value9: [],
|
||||
value10: [],
|
||||
loading: false,
|
||||
states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
|
||||
};
|
||||
@ -569,6 +580,47 @@ Enter keywords and search data from server.
|
||||
```
|
||||
:::
|
||||
|
||||
### Create new items
|
||||
Create and select new items that are not included in select options
|
||||
:::demo By using the `allow-create` attribute, users can create new items by typing in the input box. Note that for `allow-create` to work, `filterable` must be `true`.
|
||||
```html
|
||||
<template>
|
||||
<el-select
|
||||
v-model="value10"
|
||||
multiple
|
||||
filterable
|
||||
allow-create
|
||||
placeholder="Choose tags for your article">
|
||||
<el-option
|
||||
v-for="item in options5"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
options5: [{
|
||||
value: 'HTML',
|
||||
label: 'HTML'
|
||||
}, {
|
||||
value: 'CSS',
|
||||
label: 'CSS'
|
||||
}, {
|
||||
value: 'JavaScript',
|
||||
label: 'JavaScript'
|
||||
}],
|
||||
value10: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Select Attributes
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||
@ -579,6 +631,7 @@ Enter keywords and search data from server.
|
||||
| name | the name attribute of select input | string | — | — |
|
||||
| placeholder | placeholder | string | — | Select |
|
||||
| filterable | whether Select is filterable | boolean | — | false |
|
||||
| allow-create | whether creating new items is allowed. To use this, `filterable` must be true | boolean | — | false |
|
||||
| filter-method | custom filter method | function | — | — |
|
||||
| remote | whether options are loaded from server | boolean | — | false |
|
||||
| remote-method | custom remote search method | function | — | — |
|
||||
|
@ -62,6 +62,16 @@
|
||||
}]
|
||||
}],
|
||||
options4: [],
|
||||
options5: [{
|
||||
value: 'HTML',
|
||||
label: 'HTML'
|
||||
}, {
|
||||
value: 'CSS',
|
||||
label: 'CSS'
|
||||
}, {
|
||||
value: 'JavaScript',
|
||||
label: 'JavaScript'
|
||||
}],
|
||||
cities: [{
|
||||
value: 'Beijing',
|
||||
label: '北京'
|
||||
@ -90,6 +100,7 @@
|
||||
value7: '',
|
||||
value8: '',
|
||||
value9: '',
|
||||
value10: [],
|
||||
loading: false,
|
||||
states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
|
||||
};
|
||||
@ -585,6 +596,47 @@
|
||||
```
|
||||
:::
|
||||
|
||||
### 创建条目
|
||||
可以创建并选中选项中不存在的条目
|
||||
:::demo 使用`allow-create`属性即可通过在输入框中输入文字来创建新的条目。注意此时`filterable`必须为真。
|
||||
```html
|
||||
<template>
|
||||
<el-select
|
||||
v-model="value10"
|
||||
multiple
|
||||
filterable
|
||||
allow-create
|
||||
placeholder="请选择文章标签">
|
||||
<el-option
|
||||
v-for="item in options5"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
options5: [{
|
||||
value: 'HTML',
|
||||
label: 'HTML'
|
||||
}, {
|
||||
value: 'CSS',
|
||||
label: 'CSS'
|
||||
}, {
|
||||
value: 'JavaScript',
|
||||
label: 'JavaScript'
|
||||
}],
|
||||
value10: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Select Attributes
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||
@ -595,6 +647,7 @@
|
||||
| name | select input 的 name 属性 | string | — | — |
|
||||
| placeholder | 占位符 | string | — | 请选择 |
|
||||
| filterable | 是否可搜索 | boolean | — | false |
|
||||
| allow-create | 是否允许用户创建新条目,需配合 `filterable` 使用 | boolean | — | false |
|
||||
| filter-method | 自定义过滤方法 | function | — | — |
|
||||
| remote | 是否为远程搜索 | boolean | — | false |
|
||||
| remote-method | 远程搜索方法 | function | — | — |
|
||||
|
@ -85,6 +85,7 @@
|
||||
import Locale from 'element-ui/src/mixins/locale';
|
||||
import ElInput from 'element-ui/packages/input';
|
||||
import ElSelectMenu from './select-dropdown.vue';
|
||||
import ElOption from './option.vue';
|
||||
import ElTag from 'element-ui/packages/tag';
|
||||
import debounce from 'throttle-debounce/debounce';
|
||||
import Clickoutside from 'element-ui/src/utils/clickoutside';
|
||||
@ -143,7 +144,8 @@
|
||||
},
|
||||
|
||||
showNewOption() {
|
||||
let hasExistingOption = this.options.filter(option => !option.created).some(option => option.currentLabel === this.query);
|
||||
let hasExistingOption = this.options.filter(option => !option.created)
|
||||
.some(option => option.currentLabel === this.query);
|
||||
return this.filterable && this.allowCreate && this.query !== '' && !hasExistingOption;
|
||||
}
|
||||
},
|
||||
@ -151,6 +153,7 @@
|
||||
components: {
|
||||
ElInput,
|
||||
ElSelectMenu,
|
||||
ElOption,
|
||||
ElTag
|
||||
},
|
||||
|
||||
@ -248,10 +251,7 @@
|
||||
visible(val) {
|
||||
if (!val) {
|
||||
this.$refs.reference.$el.querySelector('input').blur();
|
||||
let icon = this.$el.querySelector('.el-input__icon');
|
||||
if (icon) {
|
||||
removeClass(icon, 'is-reverse');
|
||||
}
|
||||
this.handleIconHide();
|
||||
this.broadcast('ElSelectDropdown', 'destroyPopper');
|
||||
if (this.$refs.input) {
|
||||
this.$refs.input.blur();
|
||||
@ -259,21 +259,13 @@
|
||||
this.query = '';
|
||||
this.resetHoverIndex();
|
||||
if (!this.multiple) {
|
||||
if (this.dropdownUl && this.selected && this.selected.$el) {
|
||||
let selectedRect = this.selected.$el.getBoundingClientRect();
|
||||
let popperRect = this.$refs.popper.$el.getBoundingClientRect();
|
||||
this.bottomOverflowBeforeHidden = selectedRect.bottom - popperRect.bottom;
|
||||
this.topOverflowBeforeHidden = selectedRect.top - popperRect.top;
|
||||
}
|
||||
this.getOverflows();
|
||||
if (this.selected && this.selected.value) {
|
||||
this.selectedLabel = this.selected.currentLabel;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let icon = this.$el.querySelector('.el-input__icon');
|
||||
if (icon && !hasClass(icon, 'el-icon-circle-close')) {
|
||||
addClass(icon, 'is-reverse');
|
||||
}
|
||||
this.handleIconShow();
|
||||
this.broadcast('ElSelectDropdown', 'updatePopper');
|
||||
if (this.filterable) {
|
||||
this.query = this.selectedLabel;
|
||||
@ -288,15 +280,7 @@
|
||||
this.dropdownUl = [].filter.call(dropdownChildNodes, item => item.tagName === 'UL')[0];
|
||||
}
|
||||
if (!this.multiple && this.dropdownUl) {
|
||||
if (this.bottomOverflowBeforeHidden > 0) {
|
||||
this.$nextTick(() => {
|
||||
this.dropdownUl.scrollTop += this.bottomOverflowBeforeHidden;
|
||||
});
|
||||
} else if (this.topOverflowBeforeHidden < 0) {
|
||||
this.$nextTick(() => {
|
||||
this.dropdownUl.scrollTop += this.topOverflowBeforeHidden;
|
||||
});
|
||||
}
|
||||
this.setOverflow();
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -312,30 +296,64 @@
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleIconHide() {
|
||||
let icon = this.$el.querySelector('.el-input__icon');
|
||||
if (icon) {
|
||||
removeClass(icon, 'is-reverse');
|
||||
}
|
||||
},
|
||||
|
||||
handleIconShow() {
|
||||
let icon = this.$el.querySelector('.el-input__icon');
|
||||
if (icon && !hasClass(icon, 'el-icon-circle-close')) {
|
||||
addClass(icon, 'is-reverse');
|
||||
}
|
||||
},
|
||||
|
||||
getOverflows() {
|
||||
if (this.dropdownUl && this.selected && this.selected.$el) {
|
||||
let selectedRect = this.selected.$el.getBoundingClientRect();
|
||||
let popperRect = this.$refs.popper.$el.getBoundingClientRect();
|
||||
this.bottomOverflowBeforeHidden = selectedRect.bottom - popperRect.bottom;
|
||||
this.topOverflowBeforeHidden = selectedRect.top - popperRect.top;
|
||||
}
|
||||
},
|
||||
|
||||
setOverflow() {
|
||||
if (this.bottomOverflowBeforeHidden > 0) {
|
||||
this.$nextTick(() => {
|
||||
this.dropdownUl.scrollTop += this.bottomOverflowBeforeHidden;
|
||||
});
|
||||
} else if (this.topOverflowBeforeHidden < 0) {
|
||||
this.$nextTick(() => {
|
||||
this.dropdownUl.scrollTop += this.topOverflowBeforeHidden;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getSelected() {
|
||||
if (this.multiple) {
|
||||
let result = [];
|
||||
if (Array.isArray(this.value)) {
|
||||
this.value.forEach(value => {
|
||||
let option = this.options.filter(option => option.value === value)[0];
|
||||
if (option) {
|
||||
result.push(option);
|
||||
} else {
|
||||
result.push({
|
||||
value: this.value,
|
||||
currentLabel: value,
|
||||
hitState: false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (!this.multiple) {
|
||||
let option = this.options.filter(option => option.value === this.value)[0] ||
|
||||
{ value: this.value, currentLabel: this.value };
|
||||
{ value: this.value, currentLabel: this.value };
|
||||
this.selectedLabel = option.currentLabel;
|
||||
return option;
|
||||
}
|
||||
let result = [];
|
||||
if (Array.isArray(this.value)) {
|
||||
this.value.forEach(value => {
|
||||
let option = this.options.filter(option => option.value === value)[0];
|
||||
if (option) {
|
||||
result.push(option);
|
||||
} else {
|
||||
result.push({
|
||||
value: this.value,
|
||||
currentLabel: value,
|
||||
hitState: false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
handleIconClick(event) {
|
||||
@ -484,8 +502,10 @@
|
||||
},
|
||||
|
||||
resetScrollTop() {
|
||||
let bottomOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().bottom - this.$refs.popper.$el.getBoundingClientRect().bottom;
|
||||
let topOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().top - this.$refs.popper.$el.getBoundingClientRect().top;
|
||||
let bottomOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().bottom -
|
||||
this.$refs.popper.$el.getBoundingClientRect().bottom;
|
||||
let topOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().top -
|
||||
this.$refs.popper.$el.getBoundingClientRect().top;
|
||||
if (bottomOverflowDistance > 0) {
|
||||
this.dropdownUl.scrollTop += bottomOverflowDistance;
|
||||
}
|
||||
@ -541,7 +561,7 @@
|
||||
if (this.multiple && !Array.isArray(this.value)) {
|
||||
this.$emit('input', []);
|
||||
}
|
||||
if (!this.multiple && !this.value) {
|
||||
if (!this.multiple && (!this.value || Array.isArray(this.value))) {
|
||||
this.$emit('input', '');
|
||||
}
|
||||
if (this.remote) {
|
||||
|
@ -3,7 +3,7 @@ import Select from 'packages/select';
|
||||
|
||||
describe('Select', () => {
|
||||
const getSelectVm = (configs = {}, options) => {
|
||||
['multiple', 'clearable', 'filterable', 'remote'].forEach(config => {
|
||||
['multiple', 'clearable', 'filterable', 'allowCreate', 'remote'].forEach(config => {
|
||||
configs[config] = configs[config] || false;
|
||||
});
|
||||
configs.multipleLimit = configs.multipleLimit || 0;
|
||||
@ -39,6 +39,7 @@ describe('Select', () => {
|
||||
:multiple-limit="multipleLimit"
|
||||
:clearable="clearable"
|
||||
:filterable="filterable"
|
||||
:allow-create="allowCreate"
|
||||
:filterMethod="filterMethod"
|
||||
:remote="remote"
|
||||
:loading="loading"
|
||||
@ -60,6 +61,7 @@ describe('Select', () => {
|
||||
multipleLimit: configs.multipleLimit,
|
||||
clearable: configs.clearable,
|
||||
filterable: configs.filterable,
|
||||
allowCreate: configs.allowCreate,
|
||||
loading: false,
|
||||
filterMethod: configs.filterMethod && configs.filterMethod(this),
|
||||
remote: configs.remote,
|
||||
@ -352,6 +354,23 @@ describe('Select', () => {
|
||||
}, 100);
|
||||
});
|
||||
|
||||
it('allow create', done => {
|
||||
vm = getSelectVm({ filterable: true, allowCreate: true });
|
||||
const select = vm.$children[0];
|
||||
select.selectedLabel = 'new';
|
||||
select.onInputChange();
|
||||
select.visible = true;
|
||||
setTimeout(() => {
|
||||
const options = document.querySelectorAll('.el-select-dropdown__item span');
|
||||
const target = [].filter.call(options, option => option.innerText === 'new');
|
||||
target[0].click();
|
||||
setTimeout(() => {
|
||||
expect(select.value.indexOf('new') > -1).to.true;
|
||||
done();
|
||||
}, 50);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('multiple select', done => {
|
||||
vm = getSelectVm({ multiple: true });
|
||||
const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
|
||||
|
Loading…
Reference in New Issue
Block a user