Base MultiSelect Implementation
parent
38b174ff64
commit
09ebaa9b73
|
@ -48,7 +48,7 @@
|
|||
|
||||
.p-multiselect.p-disabled .p-multiselect-trigger,
|
||||
.p-multiselect.p-disabled .p-multiselect-label {
|
||||
cursor: auto
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.p-multiselect-panel {
|
||||
|
@ -69,6 +69,8 @@
|
|||
|
||||
.p-multiselect-panel .p-multiselect-list {
|
||||
border: 0 none;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.p-multiselect-panel .p-multiselect-item {
|
||||
|
@ -88,7 +90,7 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.p-multiselect-panel .p-multiselect-item label {
|
||||
.p-multiselect-panel .p-multiselect-item > span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="containerClass" @click="onClick">
|
||||
<div ref="container" :class="containerClass" @click="onClick">
|
||||
<div class="p-hidden-accessible">
|
||||
<input ref="focusInput" type="text" role="listbox" readonly :disabled="disabled" @focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :tabindex="tabindex"/>
|
||||
</div>
|
||||
|
@ -9,10 +9,32 @@
|
|||
<div class="p-multiselect-trigger">
|
||||
<span class="p-multiselect-trigger-icon pi pi-chevron-down p-c"></span>
|
||||
</div>
|
||||
<transition name="p-input-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
||||
<div ref="overlay" class="p-multiselect-panel" v-if="overlayVisible">
|
||||
<div ref="itemsWrapper" class="p-multiselect-items-wrapper" :style="{'max-height': scrollHeight}">
|
||||
<ul class="p-multiselect-items p-multiselect-list p-component">
|
||||
<li v-for="(option, i) of visibleOptions" :class="['p-multiselect-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]"
|
||||
:aria-label="getOptionLabel(option)" :key="getOptionLabel(option)" @click="onOptionSelect($event, option)" @keydown="onOptionKeyDown($event, option)" :tabindex="tabindex||'0'">
|
||||
<div class="p-checkbox p-component">
|
||||
<div :class="['p-checkbox-box p-component', {'p-highlight': isSelected(option)}]">
|
||||
<span :class="['p-checkbox-icon p-c', {'pi pi-check': isSelected(option)}]"></span>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="option" :option="option" :index="i">
|
||||
<span>{{getOptionLabel(option)}}</span>
|
||||
</slot>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ObjectUtils from '../utils/ObjectUtils';
|
||||
import DomHandler from '../utils/DomHandler';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: null,
|
||||
|
@ -58,6 +80,21 @@ export default {
|
|||
isOptionDisabled(option) {
|
||||
return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
|
||||
},
|
||||
isSelected(option) {
|
||||
let selected = false;
|
||||
let optionValue = this.getOptionValue(option);
|
||||
|
||||
if (this.value) {
|
||||
for (let val of this.value) {
|
||||
if (ObjectUtils.equals(val, optionValue, this.dataKey)) {
|
||||
selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
},
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
},
|
||||
|
@ -65,14 +102,17 @@ export default {
|
|||
this.focused = false;
|
||||
},
|
||||
onClick() {
|
||||
|
||||
if (!this.$refs.overlay || !this.$refs.overlay.contains(event.target)) {
|
||||
this.overlayVisible = !this.overlayVisible;
|
||||
this.$refs.focusInput.focus();
|
||||
}
|
||||
},
|
||||
onKeyDown(event) {
|
||||
switch(event.which) {
|
||||
//down
|
||||
case 40:
|
||||
if (this.visibleOptions && this.overlayVisible && event.altKey) {
|
||||
this.overlayVisible = true;
|
||||
this.overlayVisible = false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -88,25 +128,102 @@ export default {
|
|||
case 13:
|
||||
case 27:
|
||||
if (this.overlayVisible) {
|
||||
this.hideOverlay();
|
||||
this.overlayVisible = false;
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
//tab
|
||||
case 9:
|
||||
this.hideOverlay();
|
||||
this.overlayVisible = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
onOptionSelect(event, option) {
|
||||
if (this.disabled || this.isOptionDisabled(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let selected = this.isSelected(option);
|
||||
let valueChanged = false;
|
||||
let value = null;
|
||||
|
||||
if (selected)
|
||||
value = this.value.filter(val => !ObjectUtils.equals(val, this.getOptionValue(option), this.dataKey));
|
||||
else
|
||||
value = [...this.value || [], this.getOptionValue(option)];
|
||||
|
||||
this.$emit('input', value);
|
||||
this.$emit('change', {originalEvent: event, value: value});
|
||||
},
|
||||
onOptionKeyDown(event, option) {
|
||||
let listItem = event.target;
|
||||
|
||||
switch(event.which) {
|
||||
//down
|
||||
case 40:
|
||||
var nextItem = this.findNextItem(listItem);
|
||||
if (nextItem) {
|
||||
nextItem.focus();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
//up
|
||||
case 38:
|
||||
var prevItem = this.findPrevItem(listItem);
|
||||
if (prevItem) {
|
||||
prevItem.focus();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
//enter
|
||||
case 13:
|
||||
this.onOptionSelect(event, option);
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
findNextItem(item) {
|
||||
let nextItem = item.nextElementSibling;
|
||||
|
||||
if (nextItem)
|
||||
return DomHandler.hasClass(nextItem, 'p-disabled') ? this.findNextItem(nextItem) : nextItem;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
findPrevItem(item) {
|
||||
let prevItem = item.previousElementSibling;
|
||||
|
||||
if (prevItem)
|
||||
return DomHandler.hasClass(prevItem, 'p-disabled') ? this.findPrevItem(prevItem) : prevItem;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
onOverlayEnter() {
|
||||
this.alignOverlay();
|
||||
this.bindOutsideClickListener();
|
||||
},
|
||||
onOverlayLeave() {
|
||||
this.unbindOutsideClickListener();
|
||||
},
|
||||
alignOverlay() {
|
||||
DomHandler.relativePosition(this.$refs.overlay, this.$refs.container);
|
||||
},
|
||||
bindOutsideClickListener() {
|
||||
if (!this.outsideClickListener) {
|
||||
this.outsideClickListener = (event) => {
|
||||
if (this.$refs.overlay && !this.$refs.overlay.contains(event.target)) {
|
||||
this.hideOverlay();
|
||||
if (this.overlayVisible && this.$refs.overlay && !this.$refs.overlay.contains(event.target)) {
|
||||
this.overlayVisible = false;
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', this.outsideClickListener);
|
||||
|
@ -118,10 +235,6 @@ export default {
|
|||
this.outsideClickListener = null;
|
||||
}
|
||||
},
|
||||
hideOverlay() {
|
||||
this.overlayVisible = false;
|
||||
this.unbindOutsideClickListener();
|
||||
},
|
||||
getLabelByValue(val) {
|
||||
let label = null;
|
||||
|
||||
|
@ -174,7 +287,7 @@ export default {
|
|||
label += ',';
|
||||
}
|
||||
|
||||
label += this.findLabelByValue(this.value[i]);
|
||||
label += this.getLabelByValue(this.value[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
Loading…
Reference in New Issue