2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-19 09:50:33 +03:00

- remove selected-option-container slot

- add prop methods for getting looped slot scopes
- simplify `Deselect` component
This commit is contained in:
Jeff
2019-10-31 14:28:09 -07:00
parent 7f069a1c26
commit db8b40afab
3 changed files with 100 additions and 37 deletions
+22 -5
View File
@@ -1,5 +1,22 @@
<template> <script>
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"> export default {
<path d="M6.895455 5l2.842897-2.842898c.348864-.348863.348864-.914488 0-1.263636L9.106534.261648c-.348864-.348864-.914489-.348864-1.263636 0L5 3.104545 2.157102.261648c-.348863-.348864-.914488-.348864-1.263636 0L.261648.893466c-.348864.348864-.348864.914489 0 1.263636L3.104545 5 .261648 7.842898c-.348864.348863-.348864.914488 0 1.263636l.631818.631818c.348864.348864.914773.348864 1.263636 0L5 6.895455l2.842898 2.842897c.348863.348864.914772.348864 1.263636 0l.631818-.631818c.348864-.348864.348864-.914489 0-1.263636L6.895455 5z"/> functional: true,
</svg> render (_, {data}) {
</template> const svg = {
attrs: {
xmlns: 'http://www.w3.org/2000/svg',
width: 10,
height: 10,
},
};
const path = {
attrs: {
d: 'M6.895455 5l2.842897-2.842898c.348864-.348863.348864-.914488 0-1.263636L9.106534.261648c-.348864-.348864-.914489-.348864-1.263636 0L5 3.104545 2.157102.261648c-.348863-.348864-.914488-.348864-1.263636 0L.261648.893466c-.348864.348864-.348864.914489 0 1.263636L3.104545 5 .261648 7.842898c-.348864.348863-.348864.914488 0 1.263636l.631818.631818c.348864.348864.914773.348864 1.263636 0L5 6.895455l2.842898 2.842897c.348863.348864.914772.348864 1.263636 0l.631818-.631818c.348864-.348864.348864-.914489 0-1.263636L6.895455 5z,',
},
};
return _('button', data, [_('svg', svg, [_('path', path)])]);
},
};
</script>
+78 -20
View File
@@ -7,24 +7,19 @@
<div ref="toggle" @mousedown.prevent="toggleDropdown" class="vs__dropdown-toggle"> <div ref="toggle" @mousedown.prevent="toggleDropdown" class="vs__dropdown-toggle">
<div class="vs__selected-options" ref="selectedOptions"> <div class="vs__selected-options" ref="selectedOptions">
<slot v-for="option in selectedValue" <slot name="selected-option" v-for="option in scopedValues" v-bind="option">
name="selected-option-container" <span :class="option.bindings.class">
:option="normalizeOptionForSlot(option)" {{ option.label }}
:deselect="deselect" <component
:multiple="multiple" :is="option.deselect.component"
:disabled="disabled"> v-bind="option.deselect.bindings"
<span :key="getOptionKey(option)" class="vs__selected"> v-on="option.deselect.events"
<slot name="selected-option" v-bind="normalizeOptionForSlot(option)"> />
{{ getOptionLabel(option) }}
</slot>
<button v-if="multiple" :disabled="disabled" @click="deselect(option)" type="button" class="vs__deselect" aria-label="Deselect option">
<component :is="childComponents.Deselect" />
</button>
</span> </span>
</slot> </slot>
<slot name="search" v-bind="scope.search"> <slot name="search" v-bind="scope.search">
<input class="vs__search" v-bind="scope.search.attributes" v-on="scope.search.events"> <input v-bind="scope.search.attributes" v-on="scope.search.events">
</slot> </slot>
</div> </div>
@@ -51,9 +46,16 @@
</div> </div>
<transition :name="transition"> <transition :name="transition">
<ul ref="dropdownMenu" v-if="dropdownOpen" class="vs__dropdown-menu" role="listbox" @mousedown.prevent="onMousedown" @mouseup="onMouseUp"> <ul
<slot name="option" v-for="scope in normalizedFilteredOptions" v-bind="scope"> ref="dropdownMenu"
<li v-bind="scope.attributes" v-on="scope.events">{{ getOptionLabel(scope.option) }}</li> v-if="dropdownOpen"
class="vs__dropdown-menu"
role="listbox"
@mousedown.prevent="onMousedown"
@mouseup="onMouseUp"
>
<slot name="option" v-for="{attributes, events, option} in scopedOptions" v-bind="{attributes, events, option}">
<li v-bind="attributes" v-on="events">{{ getOptionLabel(option) }}</li>
</slot> </slot>
<li v-if="!filteredOptions.length" class="vs__no-options" @mousedown.stop=""> <li v-if="!filteredOptions.length" class="vs__no-options" @mousedown.stop="">
<slot name="no-options">Sorry, no matching options.</slot> <slot name="no-options">Sorry, no matching options.</slot>
@@ -316,6 +318,50 @@
}, },
}, },
getSelectedOptionScope: {
type: Function,
default(option, index) {
return {
label: this.getOptionLabel(option),
deselect: this.getOptionDeselectScope(option),
bindings: {
key: this.getOptionKey(option),
option: this.normalizeOptionForSlot(option),
deselect: this.deselect,
multiple: this.multiple,
class: "vs__selected",
},
events: {
'mouseover': () => this.selectable(option) ? this.typeAheadPointer = index : null,
'mousedown': e => {
e.preventDefault();
e.stopPropagation();
return this.selectable(option) ? this.select(option) : null;
}
},
};
},
},
getOptionDeselectScope: {
type: Function,
default(option) {
return {
component: childComponents.Deselect,
bindings: {
'type': 'button',
'class': 'vs__deselect',
'aria-label': `Deselect ${this.getOptionLabel(option)}`,
'disabled': this.disabled,
'multiple': this.multiple,
},
events: {
'click': () => this.deselect(option),
}
}
}
},
/** /**
* Select the current value if selectOnTab is enabled * Select the current value if selectOnTab is enabled
*/ */
@@ -917,6 +963,10 @@
return []; return [];
}, },
scopedValues() {
return this.selectedValue.map(option => this.getSelectedOptionScope(option));
},
/** /**
* The options available to be chosen * The options available to be chosen
* from the dropdown, including any * from the dropdown, including any
@@ -939,20 +989,28 @@
}, },
/** /**
* The object to be bound to the $slots.search scoped slot. * Each key of this object is the name of a scoped slot
* within the component. The value of that key is the
* object that will be passed as the scope for the
* slot. There are a few slots that take place in
* v-for loops these slots can't be captured in
* a computed prop, so there's specific methods
* for each of those slots.
*
* @returns {Object} * @returns {Object}
*/ */
scope () { scope () {
return { return {
search: { search: {
attributes: { attributes: {
'class': 'vs__search',
'disabled': this.disabled, 'disabled': this.disabled,
'placeholder': this.searchPlaceholder, 'placeholder': this.searchPlaceholder,
'tabindex': this.tabindex, 'tabindex': this.tabindex,
'readonly': !this.searchable, 'readonly': !this.searchable,
'id': this.inputId, 'id': this.inputId,
'aria-expanded': this.dropdownOpen, 'aria-expanded': this.dropdownOpen,
'aria-label': 'Search for option', 'aria-label': 'Search for an option',
'ref': 'search', 'ref': 'search',
'role': 'combobox', 'role': 'combobox',
'type': 'search', 'type': 'search',
@@ -1068,7 +1126,7 @@
return options return options
}, },
normalizedFilteredOptions() { scopedOptions() {
return this.filteredOptions.map((option, index) => this.getDropdownOptionScope(this.normalizeOptionForSlot(option), index)); return this.filteredOptions.map((option, index) => this.getDropdownOptionScope(this.normalizeOptionForSlot(option), index));
}, },
-12
View File
@@ -1,18 +1,6 @@
import { mountDefault } from '../helpers'; import { mountDefault } from '../helpers';
describe('Scoped Slots', () => { describe('Scoped Slots', () => {
it('receives an option object to the selected-option-container slot', () => {
const Select = mountDefault(
{value: 'one'},
{
scopedSlots: {
'selected-option-container': `<span slot="selected-option-container" slot-scope="{option}">{{ option.label }}</span>`,
},
});
expect(Select.find({ ref: 'selectedOptions' }).text()).toEqual('one')
});
it('receives an option object to the selected-option slot', () => { it('receives an option object to the selected-option slot', () => {
const Select = mountDefault( const Select = mountDefault(
{value: 'one'}, {value: 'one'},