Base MultiSelect Implementation

pull/12/head
cagataycivici 2018-12-30 22:20:10 +03:00
parent 38b174ff64
commit 09ebaa9b73
2 changed files with 130 additions and 15 deletions

View File

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

View File

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