Fixed #972
parent
f89d755530
commit
72dbd25535
|
@ -706,6 +706,10 @@ input.p-dropdown-label {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.p-dropdown-item-group {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.p-dropdown-items {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
|
@ -326,6 +326,10 @@ export default {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.p-listbox-item-group {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.p-listbox-filter-container {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -6,18 +6,23 @@ interface MultiSelectProps {
|
|||
optionLabel?: string;
|
||||
optionValue?: any;
|
||||
optionDisabled?: boolean;
|
||||
optionGroupLabel?: string;
|
||||
optionGroupChildren?: string;
|
||||
scrollHeight?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
filter?: boolean;
|
||||
tabindex?: string;
|
||||
inputId?: string;
|
||||
dataKey?: string;
|
||||
filter?: boolean;
|
||||
filterPlaceholder?: string;
|
||||
filterLocale?: string;
|
||||
filterMatchMode?: string;
|
||||
filterFields?: string[];
|
||||
ariaLabelledBy?: string;
|
||||
appendTo?: string;
|
||||
emptyFilterMessage?: string;
|
||||
emptyMessage?: string;
|
||||
display?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
</div>
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
||||
<div :ref="overlayRef" class="p-multiselect-panel p-component" v-if="overlayVisible">
|
||||
<slot name="header" :value="modelValue" :options="visibleOptions"></slot>
|
||||
<div class="p-multiselect-header">
|
||||
<div class="p-checkbox p-component" @click="onToggleAll" role="checkbox" :aria-checked="allSelected">
|
||||
<div class="p-hidden-accessible">
|
||||
|
@ -44,20 +45,46 @@
|
|||
</div>
|
||||
<div class="p-multiselect-items-wrapper" :style="{'max-height': scrollHeight}">
|
||||
<ul class="p-multiselect-items p-component" role="listbox" aria-multiselectable="true">
|
||||
<li v-for="(option, i) of visibleOptions" :class="['p-multiselect-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" role="option" :aria-selected="isSelected(option)"
|
||||
:aria-label="getOptionLabel(option)" :key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @keydown="onOptionKeyDown($event, option)" :tabindex="tabindex||'0'" v-ripple>
|
||||
<div class="p-checkbox p-component">
|
||||
<div :class="['p-checkbox-box', {'p-highlight': isSelected(option)}]">
|
||||
<span :class="['p-checkbox-icon', {'pi pi-check': isSelected(option)}]"></span>
|
||||
<template v-if="!optionGroupLabel">
|
||||
<li v-for="(option, i) of visibleOptions" :class="['p-multiselect-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" role="option" :aria-selected="isSelected(option)"
|
||||
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @keydown="onOptionKeyDown($event, option)" :tabindex="tabindex||'0'" :aria-label="getOptionLabel(option)" v-ripple>
|
||||
<div class="p-checkbox p-component">
|
||||
<div :class="['p-checkbox-box', {'p-highlight': isSelected(option)}]">
|
||||
<span :class="['p-checkbox-icon', {'pi pi-check': isSelected(option)}]"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="option" :option="option" :index="i">
|
||||
<span>{{getOptionLabel(option)}}</span>
|
||||
</slot>
|
||||
<slot name="option" :option="option" :index="i">
|
||||
<span>{{getOptionLabel(option)}}</span>
|
||||
</slot>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-for="(optionGroup, i) of visibleOptions" :key="getOptionGroupRenderKey(optionGroup)">
|
||||
<li class="p-multiselect-item-group">
|
||||
<slot name="optiongroup" :option="optionGroup" :index="i">{{getOptionGroupLabel(optionGroup)}}</slot>
|
||||
</li>
|
||||
<li v-for="(option, i) of getOptionGroupChildren(optionGroup)" :class="['p-multiselect-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" role="option" :aria-selected="isSelected(option)"
|
||||
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @keydown="onOptionKeyDown($event, option)" :tabindex="tabindex||'0'" :aria-label="getOptionLabel(option)" v-ripple>
|
||||
<div class="p-checkbox p-component">
|
||||
<div :class="['p-checkbox-box', {'p-highlight': isSelected(option)}]">
|
||||
<span :class="['p-checkbox-icon', {'pi pi-check': isSelected(option)}]"></span>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="option" :option="option" :index="i">
|
||||
<span>{{getOptionLabel(option)}}</span>
|
||||
</slot>
|
||||
</li>
|
||||
</template>
|
||||
</template>
|
||||
<li v-if="filterValue && (!visibleOptions || (visibleOptions && visibleOptions.length === 0))" class="p-multiselect-empty-message">
|
||||
<slot name="emptyfilter">{{emptyFilterMessageText}}</slot>
|
||||
</li>
|
||||
<li v-else-if="(!options || (options && options.length === 0))" class="p-multiselect-empty-message">
|
||||
<slot name="empty">{{emptyMessageText}}</slot>
|
||||
</li>
|
||||
<li v-if="filterValue && (!visibleOptions || (visibleOptions && visibleOptions.length === 0))" class="p-multiselect-empty-message">{{emptyFilterMessage}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<slot name="footer" :value="modelValue" :options="visibleOptions"></slot>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
@ -67,6 +94,7 @@
|
|||
import {ConnectedOverlayScrollHandler} from 'primevue/utils';
|
||||
import {ObjectUtils} from 'primevue/utils';
|
||||
import {DomHandler} from 'primevue/utils';
|
||||
import {FilterService} from 'primevue/api';
|
||||
import Ripple from 'primevue/ripple';
|
||||
|
||||
export default {
|
||||
|
@ -77,18 +105,28 @@ export default {
|
|||
optionLabel: null,
|
||||
optionValue: null,
|
||||
optionDisabled: null,
|
||||
optionGroupLabel: null,
|
||||
optionGroupChildren: null,
|
||||
scrollHeight: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
},
|
||||
placeholder: String,
|
||||
disabled: Boolean,
|
||||
filter: Boolean,
|
||||
tabindex: String,
|
||||
inputId: String,
|
||||
dataKey: null,
|
||||
filter: Boolean,
|
||||
filterPlaceholder: String,
|
||||
filterLocale: String,
|
||||
filterMatchMode: {
|
||||
type: String,
|
||||
default: 'contains'
|
||||
},
|
||||
filterFields: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
ariaLabelledBy: null,
|
||||
appendTo: {
|
||||
type: String,
|
||||
|
@ -96,7 +134,11 @@ export default {
|
|||
},
|
||||
emptyFilterMessage: {
|
||||
type: String,
|
||||
default: 'No results found'
|
||||
default: null
|
||||
},
|
||||
emptyMessage: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
display: {
|
||||
type: String,
|
||||
|
@ -136,6 +178,15 @@ export default {
|
|||
getOptionRenderKey(option) {
|
||||
return this.dataKey ? ObjectUtils.resolveFieldData(option, this.dataKey) : this.getOptionLabel(option);
|
||||
},
|
||||
getOptionGroupRenderKey(optionGroup) {
|
||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel);
|
||||
},
|
||||
getOptionGroupLabel(optionGroup) {
|
||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel);
|
||||
},
|
||||
getOptionGroupChildren(optionGroup) {
|
||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren);
|
||||
},
|
||||
isOptionDisabled(option) {
|
||||
return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
|
||||
},
|
||||
|
@ -277,7 +328,7 @@ export default {
|
|||
let nextItem = item.nextElementSibling;
|
||||
|
||||
if (nextItem)
|
||||
return DomHandler.hasClass(nextItem, 'p-disabled') ? this.findNextItem(nextItem) : nextItem;
|
||||
return DomHandler.hasClass(nextItem, 'p-disabled') || DomHandler.hasClass(nextItem, 'p-multiselect-item-group') ? this.findNextItem(nextItem) : nextItem;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
@ -285,7 +336,7 @@ export default {
|
|||
let prevItem = item.previousElementSibling;
|
||||
|
||||
if (prevItem)
|
||||
return DomHandler.hasClass(prevItem, 'p-disabled') ? this.findPrevItem(prevItem) : prevItem;
|
||||
return DomHandler.hasClass(prevItem, 'p-disabled') || DomHandler.hasClass(prevItem, 'p-multiselect-item-group') ? this.findPrevItem(prevItem) : prevItem;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
@ -366,23 +417,49 @@ export default {
|
|||
return !(this.$el.isSameNode(event.target) || this.$el.contains(event.target) || (this.overlay && this.overlay.contains(event.target)));
|
||||
},
|
||||
getLabelByValue(val) {
|
||||
let label = null;
|
||||
|
||||
let option;
|
||||
if (this.options) {
|
||||
for (let option of this.options) {
|
||||
let optionValue = this.getOptionValue(option);
|
||||
|
||||
if(ObjectUtils.equals(optionValue, val, this.equalityKey)) {
|
||||
label = this.getOptionLabel(option);
|
||||
break;
|
||||
if (this.optionGroupLabel) {
|
||||
for (let optionGroup of this.options) {
|
||||
option = this.findOptionByValue(val, this.getOptionGroupChildren(optionGroup));
|
||||
if (option) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
option = this.findOptionByValue(val, this.options);
|
||||
}
|
||||
}
|
||||
|
||||
return label;
|
||||
return option ? this.getOptionLabel(option): null;
|
||||
},
|
||||
findOptionByValue(val, list) {
|
||||
for (let option of list) {
|
||||
let optionValue = this.getOptionValue(option);
|
||||
|
||||
if(ObjectUtils.equals(optionValue, val, this.equalityKey)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
onToggleAll(event) {
|
||||
const value = this.allSelected ? [] : this.visibleOptions && this.visibleOptions.map(option => this.getOptionValue(option));
|
||||
let value = null;
|
||||
|
||||
if (this.allSelected) {
|
||||
value = [];
|
||||
}
|
||||
else if (this.visibleOptions) {
|
||||
if (this.optionGroupLabel) {
|
||||
value = [];
|
||||
this.visibleOptions.forEach(optionGroup => value = [...value, ...this.getOptionGroupChildren(optionGroup)]);
|
||||
}
|
||||
else {
|
||||
value = this.visibleOptions.map(option => this.getOptionValue(option));
|
||||
}
|
||||
}
|
||||
|
||||
this.$emit('update:modelValue', value);
|
||||
this.$emit('change', {originalEvent: event, value: value});
|
||||
|
@ -420,11 +497,25 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
visibleOptions() {
|
||||
if (this.filterValue && this.filterValue.trim().length > 0)
|
||||
return this.options.filter(option => this.getOptionLabel(option).toLocaleLowerCase(this.filterLocale).indexOf(this.filterValue.toLocaleLowerCase(this.filterLocale)) > -1);
|
||||
else
|
||||
visibleOptions() {
|
||||
if (this.filterValue) {
|
||||
if (this.optionGroupLabel) {
|
||||
let filteredGroups = [];
|
||||
for (let optgroup of this.options) {
|
||||
let filteredSubOptions = FilterService.filter(this.getOptionGroupChildren(optgroup), this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
||||
if (filteredSubOptions && filteredSubOptions.length) {
|
||||
filteredGroups.push({...optgroup, ...{items: filteredSubOptions}});
|
||||
}
|
||||
}
|
||||
return filteredGroups
|
||||
}
|
||||
else {
|
||||
return FilterService.filter(this.options, this.searchFields, this.filterValue, 'contains', this.filterLocale);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.options;
|
||||
}
|
||||
},
|
||||
containerClass() {
|
||||
return [
|
||||
|
@ -468,25 +559,54 @@ export default {
|
|||
},
|
||||
allSelected() {
|
||||
if (this.filterValue && this.filterValue.trim().length > 0) {
|
||||
let allSelected = true;
|
||||
if(this.visibleOptions.length > 0) {
|
||||
for (let option of this.visibleOptions) {
|
||||
if (!this.isSelected(option)) {
|
||||
allSelected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.visibleOptions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
allSelected = false;
|
||||
return allSelected;
|
||||
|
||||
if (this.optionGroupLabel) {
|
||||
for (let optionGroup of this.visibleOptions) {
|
||||
for (let option of this.getOptionGroupChildren(optionGroup)) {
|
||||
if (!this.isSelected(option)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let option of this.visibleOptions) {
|
||||
if (!this.isSelected(option)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return this.modelValue && this.options && (this.modelValue.length > 0 && this.modelValue.length === this.options.length);
|
||||
if (this.modelValue && this.options) {
|
||||
let optionCount = 0;
|
||||
if (this.optionGroupLabel)
|
||||
this.options.forEach(optionGroup => optionCount += this.getOptionGroupChildren(optionGroup).length);
|
||||
else
|
||||
optionCount = this.options.length;
|
||||
|
||||
return optionCount > 0 && optionCount === this.modelValue.length;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
equalityKey() {
|
||||
return this.optionValue ? null : this.dataKey;
|
||||
},
|
||||
searchFields() {
|
||||
return this.filterFields || [this.optionLabel];
|
||||
},
|
||||
emptyFilterMessageText() {
|
||||
return this.emptyFilterMessage || this.$primevue.config.locale.emptyFilterMessage;
|
||||
},
|
||||
emptyMessageText() {
|
||||
return this.emptyMessage || this.$primevue.config.locale.emptyMessage;
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
|
@ -568,6 +688,10 @@ export default {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.p-multiselect-item-group {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.p-multiselect-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -34,6 +34,52 @@ data() {
|
|||
<h5>Placeholder</h5>
|
||||
<p>Common pattern is providing an empty option as the placeholder when using native selects, however Dropdown has built-in support using the placeholder option so it is suggested to use it instead of creating an empty option.</p>
|
||||
|
||||
<h5>Grouping</h5>
|
||||
<p>Options groups are specified with the <i>optionGroupLabel</i> and <i>optionGroupChildren</i> properties.</p>
|
||||
<pre v-code.script><code>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedGroupedCity: null,
|
||||
groupedCities: [{
|
||||
label: 'Germany', code: 'DE',
|
||||
items: [
|
||||
{label: 'Berlin', value: 'Berlin'},
|
||||
{label: 'Frankfurt', value: 'Frankfurt'},
|
||||
{label: 'Hamburg', value: 'Hamburg'},
|
||||
{label: 'Munich', value: 'Munich'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'USA', code: 'US',
|
||||
items: [
|
||||
{label: 'Chicago', value: 'Chicago'},
|
||||
{label: 'Los Angeles', value: 'Los Angeles'},
|
||||
{label: 'New York', value: 'New York'},
|
||||
{label: 'San Francisco', value: 'San Francisco'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Japan', code: 'JP',
|
||||
items: [
|
||||
{label: 'Kyoto', value: 'Kyoto'},
|
||||
{label: 'Osaka', value: 'Osaka'},
|
||||
{label: 'Tokyo', value: 'Tokyo'},
|
||||
{label: 'Yokohama', value: 'Yokohama'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<pre v-code><code><template v-pre>
|
||||
<Dropdown v-model="selectedGroupedCity" :options="groupedCities"
|
||||
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items">
|
||||
</Dropdown>
|
||||
</template>
|
||||
</code></pre>
|
||||
|
||||
<h5>Filtering</h5>
|
||||
<p>Filtering allows searching items in the list using an input field at the header. In order to use filtering, enable <i>filter</i> property. By default,
|
||||
optionLabel is used when searching and <i>filterFields</i> can be used to customize the fields being utilized. Furthermore, <i>filterMatchMode</i> is available
|
||||
|
|
|
@ -38,23 +38,6 @@ data() {
|
|||
<pre v-code><code>
|
||||
<Listbox v-model="selectedCity" :options="cities" optionLabel="name" :multiple="true"/>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h5>Templating</h5>
|
||||
<p>Label of an option is used as the display text of an item by default, for custom content support define an <i>option</i> template that gets the option instance as a parameter.
|
||||
In addition <i>optiongroup</i>, <i>header</i>, <i>footer</i>, <i>emptyfilter</i> and <i>empty</i> slots are provided for further customization.</p>
|
||||
<pre v-code><code><template v-pre>
|
||||
<Listbox v-model="selectedCars" :options="cars" :multiple="true" :filter="true" optionLabel="brand" listStyle="max-height:250px" style="width:15em">
|
||||
<template #header></template>
|
||||
<template #option="slotProps">
|
||||
<div>
|
||||
<img :alt="slotProps.option.brand" :src="'demo/images/car/' + slotProps.option.brand + '.png'" />
|
||||
<span>{{slotProps.option.brand}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer></footer>
|
||||
</Listbox>
|
||||
</template>
|
||||
</code></pre>
|
||||
|
||||
<h5>Grouping</h5>
|
||||
|
@ -97,9 +80,9 @@ export default {
|
|||
</code></pre>
|
||||
|
||||
<pre v-code><code><template v-pre>
|
||||
<Listbox v-model="selectedGroupedCity" :options="groupedCities"
|
||||
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items">
|
||||
</Listbox>
|
||||
<Listbox v-model="selectedGroupedCity" :options="groupedCities"
|
||||
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items">
|
||||
</Listbox>
|
||||
</template>
|
||||
</code></pre>
|
||||
|
||||
|
@ -110,6 +93,23 @@ export default {
|
|||
<pre v-code><code>
|
||||
<Listbox v-model="selectedCity" :options="cities" optionLabel="name" :filter="true"/>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h5>Templating</h5>
|
||||
<p>Label of an option is used as the display text of an item by default, for custom content support define an <i>option</i> template that gets the option instance as a parameter.
|
||||
In addition <i>optiongroup</i>, <i>header</i>, <i>footer</i>, <i>emptyfilter</i> and <i>empty</i> slots are provided for further customization.</p>
|
||||
<pre v-code><code><template v-pre>
|
||||
<Listbox v-model="selectedCars" :options="cars" :multiple="true" :filter="true" optionLabel="brand" listStyle="max-height:250px" style="width:15em">
|
||||
<template #header></template>
|
||||
<template #option="slotProps">
|
||||
<div>
|
||||
<img :alt="slotProps.option.brand" :src="'demo/images/car/' + slotProps.option.brand + '.png'" />
|
||||
<span>{{slotProps.option.brand}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer></footer>
|
||||
</Listbox>
|
||||
</template>
|
||||
</code></pre>
|
||||
|
||||
<h5>Properties</h5>
|
||||
|
|
|
@ -11,10 +11,20 @@
|
|||
<div class="content-section implementation">
|
||||
<div class="card">
|
||||
<h5>Basic</h5>
|
||||
<MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select a City" />
|
||||
<MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select Cities" />
|
||||
|
||||
<h5>Chips</h5>
|
||||
<MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select a City" display="chip" />
|
||||
<MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select Cities" display="chip" />
|
||||
|
||||
<h5>Grouped</h5>
|
||||
<MultiSelect v-model="selectedGroupedCities" :options="groupedCities" optionLabel="label" optionGroupLabel="label" optionGroupChildren="items" placeholder="Select Cities">
|
||||
<template #optiongroup="slotProps">
|
||||
<div class="p-d-flex p-ai-center country-item">
|
||||
<img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + slotProps.option.code.toLowerCase()" width="18" />
|
||||
<div>{{slotProps.option.label}}</div>
|
||||
</div>
|
||||
</template>
|
||||
</MultiSelect>
|
||||
|
||||
<h5>Advanced with Templating and Filtering</h5>
|
||||
<MultiSelect v-model="selectedCountries" :options="countries" optionLabel="name" placeholder="Select Countries" :filter="true" class="multiselect-custom">
|
||||
|
@ -50,6 +60,7 @@ export default {
|
|||
selectedCities1: null,
|
||||
selectedCities2: null,
|
||||
selectedCountries: null,
|
||||
selectedGroupedCities: null,
|
||||
cities: [
|
||||
{name: 'New York', code: 'NY'},
|
||||
{name: 'Rome', code: 'RM'},
|
||||
|
@ -68,7 +79,34 @@ export default {
|
|||
{name: 'Japan', code: 'JP'},
|
||||
{name: 'Spain', code: 'ES'},
|
||||
{name: 'United States', code: 'US'}
|
||||
]
|
||||
],
|
||||
groupedCities: [{
|
||||
label: 'Germany', code: 'DE',
|
||||
items: [
|
||||
{label: 'Berlin', value: 'Berlin'},
|
||||
{label: 'Frankfurt', value: 'Frankfurt'},
|
||||
{label: 'Hamburg', value: 'Hamburg'},
|
||||
{label: 'Munich', value: 'Munich'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'USA', code: 'US',
|
||||
items: [
|
||||
{label: 'Chicago', value: 'Chicago'},
|
||||
{label: 'Los Angeles', value: 'Los Angeles'},
|
||||
{label: 'New York', value: 'New York'},
|
||||
{label: 'San Francisco', value: 'San Francisco'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Japan', code: 'JP',
|
||||
items: [
|
||||
{label: 'Kyoto', value: 'Kyoto'},
|
||||
{label: 'Osaka', value: 'Osaka'},
|
||||
{label: 'Tokyo', value: 'Tokyo'},
|
||||
{label: 'Yokohama', value: 'Yokohama'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -79,7 +117,7 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.p-multiselect {
|
||||
min-width: 15rem;
|
||||
width: 18rem;
|
||||
}
|
||||
|
||||
::v-deep(.multiselect-custom) {
|
||||
|
@ -101,4 +139,10 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.p-multiselect {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -42,11 +42,68 @@ data() {
|
|||
|
||||
</code></pre>
|
||||
|
||||
<h5>Custom Content</h5>
|
||||
<p>Label of an option is used as the display text of an item by default, for custom content support define an <i>option</i> template that gets the option instance as a parameter.</p>
|
||||
<p>In addition the <i>value</i> template can be used to customize the selected values display instead of the default comma separated list.</p>
|
||||
<h5>Grouping</h5>
|
||||
<p>Options groups are specified with the <i>optionGroupLabel</i> and <i>optionGroupChildren</i> properties.</p>
|
||||
<pre v-code.script><code>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedGroupedCities: null,
|
||||
groupedCities: [{
|
||||
label: 'Germany', code: 'DE',
|
||||
items: [
|
||||
{label: 'Berlin', value: 'Berlin'},
|
||||
{label: 'Frankfurt', value: 'Frankfurt'},
|
||||
{label: 'Hamburg', value: 'Hamburg'},
|
||||
{label: 'Munich', value: 'Munich'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'USA', code: 'US',
|
||||
items: [
|
||||
{label: 'Chicago', value: 'Chicago'},
|
||||
{label: 'Los Angeles', value: 'Los Angeles'},
|
||||
{label: 'New York', value: 'New York'},
|
||||
{label: 'San Francisco', value: 'San Francisco'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Japan', code: 'JP',
|
||||
items: [
|
||||
{label: 'Kyoto', value: 'Kyoto'},
|
||||
{label: 'Osaka', value: 'Osaka'},
|
||||
{label: 'Tokyo', value: 'Tokyo'},
|
||||
{label: 'Yokohama', value: 'Yokohama'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<pre v-code><code><template v-pre>
|
||||
<MultiSelect v-model="selectedGroupedCities" :options="groupedCities"
|
||||
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items">
|
||||
</MultiSelect>
|
||||
</template>
|
||||
</code></pre>
|
||||
|
||||
<h5>Filtering</h5>
|
||||
<p>Filtering allows searching items in the list using an input field at the header. In order to use filtering, enable <i>filter</i> property. By default,
|
||||
optionLabel is used when searching and <i>filterFields</i> can be used to customize the fields being utilized. Furthermore, <i>filterMatchMode</i> is available
|
||||
to define the search algorithm. Valid values are "contains" (default), "startsWith" and "endsWith".</p>
|
||||
|
||||
<pre v-code><code>
|
||||
<MultiSelect v-model="selectedCars" :options="cars" :filter="true" optionLabel="brand" placeholder="Select Brands"/>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h5>Templating</h5>
|
||||
<p>Label of an option is used as the display text of an item by default, for custom content support define an <i>option</i> template that gets the option instance as a parameter.
|
||||
In addition <i>value</i>, <i>optiongroup</i>, <i>header</i>, <i>footer</i>, <i>emptyfilter</i> and <i>empty</i> slots are provided for further customization.</p>
|
||||
<pre v-code><code><template v-pre>
|
||||
<MultiSelect v-model="selectedCars2" :options="cars" optionLabel="brand" placeholder="Select a Car">
|
||||
<template #header></template>
|
||||
<template #value="slotProps">
|
||||
<div class="p-multiselect-car-token" v-for="option of slotProps.value" :key="option.brand">
|
||||
<img :alt="option.brand" :src="'demo/images/car/' + option.brand + '.png'" />
|
||||
|
@ -62,15 +119,9 @@ data() {
|
|||
<span>{{slotProps.option.brand}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer></template>
|
||||
</MultiSelect>
|
||||
</template>
|
||||
</code></pre>
|
||||
|
||||
<h5>Filter</h5>
|
||||
<p>Filtering allows searching items in the list using an input field at the header. In order to use filtering, enable the <i>filter</i> property.</p>
|
||||
<pre v-code><code>
|
||||
<MultiSelect v-model="selectedCars" :options="cars" :filter="true" optionLabel="brand" placeholder="Select Brands"/>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h5>Properties</h5>
|
||||
|
@ -116,12 +167,54 @@ data() {
|
|||
<td>null</td>
|
||||
<td>Property name or getter function to use as the disabled flag of an option, defaults to false when not defined.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>optionGroupLabel</td>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
<td>Property name or getter function to use as the label of an option group.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>optionGroupChildren</td>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
<td>Property name or getter function that refers to the children options of option group.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>scrollHeight</td>
|
||||
<td>string</td>
|
||||
<td>200px</td>
|
||||
<td>Height of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filter</td>
|
||||
<td>boolean</td>
|
||||
<td>false</td>
|
||||
<td>When specified, displays a filter input at header.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filterPlaceholder</td>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
<td>Placeholder text to show when filter input is empty.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filterLocale</td>
|
||||
<td>string</td>
|
||||
<td>undefined</td>
|
||||
<td>Locale to use in filtering. The default locale is the host environment's current locale.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filterMatchMode</td>
|
||||
<td>string</td>
|
||||
<td>contains</td>
|
||||
<td>Defines the filtering algorithm to use when searching the options. Valid values are "contains" (default), "startsWith" and "endsWith"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filterFields</td>
|
||||
<td>array</td>
|
||||
<td>null</td>
|
||||
<td>Fields used when filtering the options, defaults to optionLabel.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>placeholder</td>
|
||||
<td>string</td>
|
||||
|
@ -134,12 +227,6 @@ data() {
|
|||
<td>false</td>
|
||||
<td>When present, it specifies that the component should be disabled.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filter</td>
|
||||
<td>boolean</td>
|
||||
<td>false</td>
|
||||
<td>When specified, displays an input field to filter the items on keyup.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tabindex</td>
|
||||
<td>string</td>
|
||||
|
@ -158,18 +245,6 @@ data() {
|
|||
<td>null</td>
|
||||
<td>A property to uniquely identify an option.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filterPlaceholder</td>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
<td>Placeholder text to show when filter input is empty.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>filterLocale</td>
|
||||
<td>string</td>
|
||||
<td>undefined</td>
|
||||
<td>Locale to use in filtering. The default locale is the host environment's current locale.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ariaLabelledBy</td>
|
||||
<td>string</td>
|
||||
|
@ -186,7 +261,13 @@ data() {
|
|||
<td>emptyFilterMessage</td>
|
||||
<td>string</td>
|
||||
<td>No results found</td>
|
||||
<td>Text to display when filtering does not return any results.</td>
|
||||
<td>Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>emptyMessage</td>
|
||||
<td>string</td>
|
||||
<td>No results found</td>
|
||||
<td>Text to display when there are no options available. Defaults to value from PrimeVue locale configuration.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>display</td>
|
||||
|
@ -270,6 +351,53 @@ data() {
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<h5>Slots</h5>
|
||||
<div class="doc-tablewrapper">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Parameters</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>option</td>
|
||||
<td>option: Option instance <br />
|
||||
index: Index of the option</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>optiongroup</td>
|
||||
<td>option: OptionGroup instance <br />
|
||||
index: Index of the option group</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>value</td>
|
||||
<td>value: Value of the component <br />
|
||||
placeholder: Placeholder prop value</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>header</td>
|
||||
<td>value: Value of the component <br />
|
||||
options: Displayed options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>footer</td>
|
||||
<td>value: Value of the component <br />
|
||||
options: Displayed options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>emptyfilter</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>empty</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h5>Styling</h5>
|
||||
<p>Following is the list of structural style classes, for theming classes visit <router-link to="/theming">theming</router-link> page.</p>
|
||||
<div class="doc-tablewrapper">
|
||||
|
@ -331,10 +459,20 @@ data() {
|
|||
|
||||
<pre v-code><code><template v-pre>
|
||||
<h5>Basic</h5>
|
||||
<MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select a City" />
|
||||
<MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select Cities" />
|
||||
|
||||
<h5>Chips</h5>
|
||||
<MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select a City" display="chip"/>
|
||||
<MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select Cities" display="chip" />
|
||||
|
||||
<h5>Grouped</h5>
|
||||
<MultiSelect v-model="selectedGroupedCities" :options="groupedCities" optionLabel="label" optionGroupLabel="label" optionGroupChildren="items" placeholder="Select Cities">
|
||||
<template #optiongroup="slotProps">
|
||||
<div class="p-d-flex p-ai-center country-item">
|
||||
<img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + slotProps.option.code.toLowerCase()" width="18" />
|
||||
<div>{{slotProps.option.label}}</div>
|
||||
</div>
|
||||
</template>
|
||||
</MultiSelect>
|
||||
|
||||
<h5>Advanced with Templating and Filtering</h5>
|
||||
<MultiSelect v-model="selectedCountries" :options="countries" optionLabel="name" placeholder="Select Countries" :filter="true" class="multiselect-custom">
|
||||
|
@ -364,6 +502,7 @@ export default {
|
|||
selectedCities1: null,
|
||||
selectedCities2: null,
|
||||
selectedCountries: null,
|
||||
selectedGroupedCities: null,
|
||||
cities: [
|
||||
{name: 'New York', code: 'NY'},
|
||||
{name: 'Rome', code: 'RM'},
|
||||
|
@ -382,7 +521,34 @@ export default {
|
|||
{name: 'Japan', code: 'JP'},
|
||||
{name: 'Spain', code: 'ES'},
|
||||
{name: 'United States', code: 'US'}
|
||||
]
|
||||
],
|
||||
groupedCities: [{
|
||||
label: 'Germany', code: 'DE',
|
||||
items: [
|
||||
{label: 'Berlin', value: 'Berlin'},
|
||||
{label: 'Frankfurt', value: 'Frankfurt'},
|
||||
{label: 'Hamburg', value: 'Hamburg'},
|
||||
{label: 'Munich', value: 'Munich'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'USA', code: 'US',
|
||||
items: [
|
||||
{label: 'Chicago', value: 'Chicago'},
|
||||
{label: 'Los Angeles', value: 'Los Angeles'},
|
||||
{label: 'New York', value: 'New York'},
|
||||
{label: 'San Francisco', value: 'San Francisco'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Japan', code: 'JP',
|
||||
items: [
|
||||
{label: 'Kyoto', value: 'Kyoto'},
|
||||
{label: 'Osaka', value: 'Osaka'},
|
||||
{label: 'Tokyo', value: 'Tokyo'},
|
||||
{label: 'Yokohama', value: 'Yokohama'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -432,16 +598,26 @@ export default {
|
|||
<div class="content-section implementation">
|
||||
<div class="card">
|
||||
<h5>Basic</h5>
|
||||
<MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select a City" />
|
||||
<MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select Cities" />
|
||||
|
||||
<h5>Chips</h5>
|
||||
<MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select a City" display="chip" />
|
||||
<MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select Cities" display="chip" />
|
||||
|
||||
<h5>Grouped</h5>
|
||||
<MultiSelect v-model="selectedGroupedCities" :options="groupedCities" optionLabel="label" optionGroupLabel="label" optionGroupChildren="items" placeholder="Select Cities">
|
||||
<template #optiongroup="slotProps">
|
||||
<div class="p-d-flex p-ai-center country-item">
|
||||
<img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + slotProps.option.code.toLowerCase()" width="18" />
|
||||
<div>{{slotProps.option.label}}</div>
|
||||
</div>
|
||||
</template>
|
||||
</MultiSelect>
|
||||
|
||||
<h5>Advanced with Templating and Filtering</h5>
|
||||
<MultiSelect v-model="selectedCountries" :options="countries" optionLabel="name" placeholder="Select Countries" :filter="true" class="multiselect-custom">
|
||||
<template #value="slotProps">
|
||||
<div class="country-item country-item-value" v-for="option of slotProps.value" :key="option.code">
|
||||
<img src="https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png" />
|
||||
<img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + option.code.toLowerCase()" />
|
||||
<div>{{option.name}}</div>
|
||||
</div>
|
||||
<template v-if="!slotProps.value || slotProps.value.length === 0">
|
||||
|
@ -450,7 +626,7 @@ export default {
|
|||
</template>
|
||||
<template #option="slotProps">
|
||||
<div class="country-item">
|
||||
<img src="https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png" />
|
||||
<img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + slotProps.option.code.toLowerCase()" />
|
||||
<div>{{slotProps.option.name}}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -467,6 +643,7 @@ export default {
|
|||
selectedCities1: null,
|
||||
selectedCities2: null,
|
||||
selectedCountries: null,
|
||||
selectedGroupedCities: null,
|
||||
cities: [
|
||||
{name: 'New York', code: 'NY'},
|
||||
{name: 'Rome', code: 'RM'},
|
||||
|
@ -485,13 +662,40 @@ export default {
|
|||
{name: 'Japan', code: 'JP'},
|
||||
{name: 'Spain', code: 'ES'},
|
||||
{name: 'United States', code: 'US'}
|
||||
]
|
||||
],
|
||||
groupedCities: [{
|
||||
label: 'Germany', code: 'DE',
|
||||
items: [
|
||||
{label: 'Berlin', value: 'Berlin'},
|
||||
{label: 'Frankfurt', value: 'Frankfurt'},
|
||||
{label: 'Hamburg', value: 'Hamburg'},
|
||||
{label: 'Munich', value: 'Munich'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'USA', code: 'US',
|
||||
items: [
|
||||
{label: 'Chicago', value: 'Chicago'},
|
||||
{label: 'Los Angeles', value: 'Los Angeles'},
|
||||
{label: 'New York', value: 'New York'},
|
||||
{label: 'San Francisco', value: 'San Francisco'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Japan', code: 'JP',
|
||||
items: [
|
||||
{label: 'Kyoto', value: 'Kyoto'},
|
||||
{label: 'Osaka', value: 'Osaka'},
|
||||
{label: 'Tokyo', value: 'Tokyo'},
|
||||
{label: 'Yokohama', value: 'Yokohama'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}`,
|
||||
style: `<style lang="scss" scoped>
|
||||
.p-multiselect {
|
||||
min-width: 15rem;
|
||||
width: 18rem;
|
||||
}
|
||||
|
||||
::v-deep(.multiselect-custom) {
|
||||
|
@ -507,11 +711,16 @@ export default {
|
|||
margin-right: .5rem;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--primary-color-text);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 17px;
|
||||
margin-right: 0.5rem;
|
||||
img.flag {
|
||||
width: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.p-multiselect {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>`
|
||||
|
|
Loading…
Reference in New Issue