Base MultiSelect Implementation
parent
38b174ff64
commit
09ebaa9b73
|
@ -27,7 +27,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-multiselect .p-multiselect-label-container {
|
.p-multiselect .p-multiselect-label-container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
.p-multiselect.p-disabled .p-multiselect-trigger,
|
.p-multiselect.p-disabled .p-multiselect-trigger,
|
||||||
.p-multiselect.p-disabled .p-multiselect-label {
|
.p-multiselect.p-disabled .p-multiselect-label {
|
||||||
cursor: auto
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-multiselect-panel {
|
.p-multiselect-panel {
|
||||||
|
@ -69,6 +69,8 @@
|
||||||
|
|
||||||
.p-multiselect-panel .p-multiselect-list {
|
.p-multiselect-panel .p-multiselect-list {
|
||||||
border: 0 none;
|
border: 0 none;
|
||||||
|
margin: 0;
|
||||||
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-multiselect-panel .p-multiselect-item {
|
.p-multiselect-panel .p-multiselect-item {
|
||||||
|
@ -88,7 +90,7 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-multiselect-panel .p-multiselect-item label {
|
.p-multiselect-panel .p-multiselect-item > span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="containerClass" @click="onClick">
|
<div ref="container" :class="containerClass" @click="onClick">
|
||||||
<div class="p-hidden-accessible">
|
<div class="p-hidden-accessible">
|
||||||
<input ref="focusInput" type="text" role="listbox" readonly :disabled="disabled" @focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :tabindex="tabindex"/>
|
<input ref="focusInput" type="text" role="listbox" readonly :disabled="disabled" @focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :tabindex="tabindex"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,10 +9,32 @@
|
||||||
<div class="p-multiselect-trigger">
|
<div class="p-multiselect-trigger">
|
||||||
<span class="p-multiselect-trigger-icon pi pi-chevron-down p-c"></span>
|
<span class="p-multiselect-trigger-icon pi pi-chevron-down p-c"></span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ObjectUtils from '../utils/ObjectUtils';
|
||||||
|
import DomHandler from '../utils/DomHandler';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: null,
|
value: null,
|
||||||
|
@ -58,6 +80,21 @@ export default {
|
||||||
isOptionDisabled(option) {
|
isOptionDisabled(option) {
|
||||||
return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
|
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() {
|
onFocus() {
|
||||||
this.focused = true;
|
this.focused = true;
|
||||||
},
|
},
|
||||||
|
@ -65,14 +102,17 @@ export default {
|
||||||
this.focused = false;
|
this.focused = false;
|
||||||
},
|
},
|
||||||
onClick() {
|
onClick() {
|
||||||
|
if (!this.$refs.overlay || !this.$refs.overlay.contains(event.target)) {
|
||||||
|
this.overlayVisible = !this.overlayVisible;
|
||||||
|
this.$refs.focusInput.focus();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onKeyDown(event) {
|
onKeyDown(event) {
|
||||||
switch(event.which) {
|
switch(event.which) {
|
||||||
//down
|
//down
|
||||||
case 40:
|
case 40:
|
||||||
if (this.visibleOptions && this.overlayVisible && event.altKey) {
|
if (this.visibleOptions && this.overlayVisible && event.altKey) {
|
||||||
this.overlayVisible = true;
|
this.overlayVisible = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -88,25 +128,102 @@ export default {
|
||||||
case 13:
|
case 13:
|
||||||
case 27:
|
case 27:
|
||||||
if (this.overlayVisible) {
|
if (this.overlayVisible) {
|
||||||
this.hideOverlay();
|
this.overlayVisible = false;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//tab
|
//tab
|
||||||
case 9:
|
case 9:
|
||||||
this.hideOverlay();
|
this.overlayVisible = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
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() {
|
bindOutsideClickListener() {
|
||||||
if (!this.outsideClickListener) {
|
if (!this.outsideClickListener) {
|
||||||
this.outsideClickListener = (event) => {
|
this.outsideClickListener = (event) => {
|
||||||
if (this.$refs.overlay && !this.$refs.overlay.contains(event.target)) {
|
if (this.overlayVisible && this.$refs.overlay && !this.$refs.overlay.contains(event.target)) {
|
||||||
this.hideOverlay();
|
this.overlayVisible = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener('click', this.outsideClickListener);
|
document.addEventListener('click', this.outsideClickListener);
|
||||||
|
@ -118,10 +235,6 @@ export default {
|
||||||
this.outsideClickListener = null;
|
this.outsideClickListener = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideOverlay() {
|
|
||||||
this.overlayVisible = false;
|
|
||||||
this.unbindOutsideClickListener();
|
|
||||||
},
|
|
||||||
getLabelByValue(val) {
|
getLabelByValue(val) {
|
||||||
let label = null;
|
let label = null;
|
||||||
|
|
||||||
|
@ -174,7 +287,7 @@ export default {
|
||||||
label += ',';
|
label += ',';
|
||||||
}
|
}
|
||||||
|
|
||||||
label += this.findLabelByValue(this.value[i]);
|
label += this.getLabelByValue(this.value[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in New Issue