pull/978/head
Cagatay Civici 2021-02-15 17:05:02 +03:00
parent f89d755530
commit 72dbd25535
8 changed files with 543 additions and 107 deletions

View File

@ -706,6 +706,10 @@ input.p-dropdown-label {
overflow: hidden;
}
.p-dropdown-item-group {
cursor: auto;
}
.p-dropdown-items {
margin: 0;
padding: 0;

View File

@ -326,6 +326,10 @@ export default {
overflow: hidden;
}
.p-listbox-item-group {
cursor: auto;
}
.p-listbox-filter-container {
position: relative;
}

View File

@ -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;
}

View File

@ -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,8 +45,9 @@
</div>
<div class="p-multiselect-items-wrapper" :style="{'max-height': scrollHeight}">
<ul class="p-multiselect-items p-component" role="listbox" aria-multiselectable="true">
<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)"
:aria-label="getOptionLabel(option)" :key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @keydown="onOptionKeyDown($event, option)" :tabindex="tabindex||'0'" v-ripple>
: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>
@ -55,9 +57,34 @@
<span>{{getOptionLabel(option)}}</span>
</slot>
</li>
<li v-if="filterValue && (!visibleOptions || (visibleOptions && visibleOptions.length === 0))" class="p-multiselect-empty-message">{{emptyFilterMessage}}</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>
</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);
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});
@ -421,10 +498,24 @@ 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
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.visibleOptions.length === 0) {
return false;
}
if (this.optionGroupLabel) {
for (let optionGroup of this.visibleOptions) {
for (let option of this.getOptionGroupChildren(optionGroup)) {
if (!this.isSelected(option)) {
allSelected = false;
break;
return false;
}
}
}
else
allSelected = false;
return allSelected;
}
else {
return this.modelValue && this.options && (this.modelValue.length > 0 && this.modelValue.length === this.options.length);
for (let option of this.visibleOptions) {
if (!this.isSelected(option)) {
return false;
}
}
}
return true;
}
else {
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;

View File

@ -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>
&lt;Dropdown v-model="selectedGroupedCity" :options="groupedCities"
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items"&gt;
&lt;/Dropdown&gt;
</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

View File

@ -38,23 +38,6 @@ data() {
<pre v-code><code>
&lt;Listbox v-model="selectedCity" :options="cities" optionLabel="name" :multiple="true"/&gt;
</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>
&lt;Listbox v-model="selectedCars" :options="cars" :multiple="true" :filter="true" optionLabel="brand" listStyle="max-height:250px" style="width:15em"&gt;
&lt;template #header&gt;&lt;/template&gt;
&lt;template #option="slotProps"&gt;
&lt;div&gt;
&lt;img :alt="slotProps.option.brand" :src="'demo/images/car/' + slotProps.option.brand + '.png'" /&gt;
&lt;span&gt;{{slotProps.option.brand}}&lt;/span&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;template #footer&gt;&lt;/footer&gt;
&lt;/Listbox&gt;
</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>
&lt;Listbox v-model="selectedGroupedCity" :options="groupedCities"
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items"&gt;
&lt;/Listbox&gt;
</template>
</code></pre>
@ -110,6 +93,23 @@ export default {
<pre v-code><code>
&lt;Listbox v-model="selectedCity" :options="cities" optionLabel="name" :filter="true"/&gt;
</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>
&lt;Listbox v-model="selectedCars" :options="cars" :multiple="true" :filter="true" optionLabel="brand" listStyle="max-height:250px" style="width:15em"&gt;
&lt;template #header&gt;&lt;/template&gt;
&lt;template #option="slotProps"&gt;
&lt;div&gt;
&lt;img :alt="slotProps.option.brand" :src="'demo/images/car/' + slotProps.option.brand + '.png'" /&gt;
&lt;span&gt;{{slotProps.option.brand}}&lt;/span&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;template #footer&gt;&lt;/footer&gt;
&lt;/Listbox&gt;
</template>
</code></pre>
<h5>Properties</h5>

View File

@ -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>

View File

@ -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>
&lt;MultiSelect v-model="selectedGroupedCities" :options="groupedCities"
optionLabel="label" optionGroupLabel="label" optionGroupChildren="items"&gt;
&lt;/MultiSelect&gt;
</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>
&lt;MultiSelect v-model="selectedCars" :options="cars" :filter="true" optionLabel="brand" placeholder="Select Brands"/&gt;
</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>
&lt;MultiSelect v-model="selectedCars2" :options="cars" optionLabel="brand" placeholder="Select a Car"&gt;
&lt;template #header&gt;&lt;/template&gt;
&lt;template #value="slotProps"&gt;
&lt;div class="p-multiselect-car-token" v-for="option of slotProps.value" :key="option.brand"&gt;
&lt;img :alt="option.brand" :src="'demo/images/car/' + option.brand + '.png'" /&gt;
@ -62,15 +119,9 @@ data() {
&lt;span&gt;{{slotProps.option.brand}}&lt;/span&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;template #footer&gt;&lt;/template&gt;
&lt;/MultiSelect&gt;
</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>
&lt;MultiSelect v-model="selectedCars" :options="cars" :filter="true" optionLabel="brand" placeholder="Select Brands"/&gt;
</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>
&lt;h5&gt;Basic&lt;/h5&gt;
&lt;MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select a City" /&gt;
&lt;MultiSelect v-model="selectedCities1" :options="cities" optionLabel="name" placeholder="Select Cities" /&gt;
&lt;h5&gt;Chips&lt;/h5&gt;
&lt;MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select a City" display="chip"/&gt;
&lt;MultiSelect v-model="selectedCities2" :options="cities" optionLabel="name" placeholder="Select Cities" display="chip" /&gt;
&lt;h5&gt;Grouped&lt;/h5&gt;
&lt;MultiSelect v-model="selectedGroupedCities" :options="groupedCities" optionLabel="label" optionGroupLabel="label" optionGroupChildren="items" placeholder="Select Cities"&gt;
&lt;template #optiongroup="slotProps"&gt;
&lt;div class="p-d-flex p-ai-center country-item"&gt;
&lt;img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + slotProps.option.code.toLowerCase()" width="18" /&gt;
&lt;div&gt;{{slotProps.option.label}}&lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;/MultiSelect&gt;
&lt;h5&gt;Advanced with Templating and Filtering&lt;/h5&gt;
&lt;MultiSelect v-model="selectedCountries" :options="countries" optionLabel="name" placeholder="Select Countries" :filter="true" class="multiselect-custom"&gt;
@ -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 {
img.flag {
width: 17px;
margin-right: 0.5rem;
}
}
}
@media screen and (max-width: 640px) {
.p-multiselect {
width: 100%;
}
}
</style>`