mirror of
https://github.com/tenrok/vue-select.git
synced 2026-05-29 05:14:04 +03:00
- remove selected-option-container slot
- add prop methods for getting looped slot scopes - simplify `Deselect` component
This commit is contained in:
@@ -1,5 +1,22 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
||||
<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"/>
|
||||
</svg>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
functional: true,
|
||||
render (_, {data}) {
|
||||
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
@@ -7,24 +7,19 @@
|
||||
<div ref="toggle" @mousedown.prevent="toggleDropdown" class="vs__dropdown-toggle">
|
||||
|
||||
<div class="vs__selected-options" ref="selectedOptions">
|
||||
<slot v-for="option in selectedValue"
|
||||
name="selected-option-container"
|
||||
:option="normalizeOptionForSlot(option)"
|
||||
:deselect="deselect"
|
||||
:multiple="multiple"
|
||||
:disabled="disabled">
|
||||
<span :key="getOptionKey(option)" class="vs__selected">
|
||||
<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>
|
||||
<slot name="selected-option" v-for="option in scopedValues" v-bind="option">
|
||||
<span :class="option.bindings.class">
|
||||
{{ option.label }}
|
||||
<component
|
||||
:is="option.deselect.component"
|
||||
v-bind="option.deselect.bindings"
|
||||
v-on="option.deselect.events"
|
||||
/>
|
||||
</span>
|
||||
</slot>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
@@ -51,9 +46,16 @@
|
||||
</div>
|
||||
|
||||
<transition :name="transition">
|
||||
<ul ref="dropdownMenu" v-if="dropdownOpen" class="vs__dropdown-menu" role="listbox" @mousedown.prevent="onMousedown" @mouseup="onMouseUp">
|
||||
<slot name="option" v-for="scope in normalizedFilteredOptions" v-bind="scope">
|
||||
<li v-bind="scope.attributes" v-on="scope.events">{{ getOptionLabel(scope.option) }}</li>
|
||||
<ul
|
||||
ref="dropdownMenu"
|
||||
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>
|
||||
<li v-if="!filteredOptions.length" class="vs__no-options" @mousedown.stop="">
|
||||
<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
|
||||
*/
|
||||
@@ -917,6 +963,10 @@
|
||||
return [];
|
||||
},
|
||||
|
||||
scopedValues() {
|
||||
return this.selectedValue.map(option => this.getSelectedOptionScope(option));
|
||||
},
|
||||
|
||||
/**
|
||||
* The options available to be chosen
|
||||
* 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}
|
||||
*/
|
||||
scope () {
|
||||
return {
|
||||
search: {
|
||||
attributes: {
|
||||
'class': 'vs__search',
|
||||
'disabled': this.disabled,
|
||||
'placeholder': this.searchPlaceholder,
|
||||
'tabindex': this.tabindex,
|
||||
'readonly': !this.searchable,
|
||||
'id': this.inputId,
|
||||
'aria-expanded': this.dropdownOpen,
|
||||
'aria-label': 'Search for option',
|
||||
'aria-label': 'Search for an option',
|
||||
'ref': 'search',
|
||||
'role': 'combobox',
|
||||
'type': 'search',
|
||||
@@ -1068,7 +1126,7 @@
|
||||
return options
|
||||
},
|
||||
|
||||
normalizedFilteredOptions() {
|
||||
scopedOptions() {
|
||||
return this.filteredOptions.map((option, index) => this.getDropdownOptionScope(this.normalizeOptionForSlot(option), index));
|
||||
},
|
||||
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { mountDefault } from '../helpers';
|
||||
|
||||
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', () => {
|
||||
const Select = mountDefault(
|
||||
{value: 'one'},
|
||||
|
||||
Reference in New Issue
Block a user