201 строка
4.0 KiB
Vue
201 строка
4.0 KiB
Vue
<template>
|
|
<div class="select">
|
|
<button class="button" @click="menuShown = !menuShown">
|
|
<span class="text">
|
|
{{ selectedItemKeys.length ? selectedNames.join(', ') : placeholder }}
|
|
</span>
|
|
<fa icon="search" size="lg" area-hidden="true" />
|
|
</button>
|
|
<transition name="fade">
|
|
<div v-if="menuShown" class="menu">
|
|
<label
|
|
v-for="item in items"
|
|
:key="item.key"
|
|
class="item"
|
|
:class="{ selected: selectedItemKeys.includes(item.key) }"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
class="item__checkbox"
|
|
@change="onChange($event, item.key)"
|
|
/>
|
|
{{ item.name }}
|
|
</label>
|
|
</div>
|
|
</transition>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import Vue, { PropOptions } from 'vue'
|
|
|
|
export default Vue.extend({
|
|
props: {
|
|
items: {
|
|
type: Array,
|
|
required: true,
|
|
} as PropOptions<{ key: string; name: string }[]>,
|
|
placeholder: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
selectedItemKeys: [] as string[],
|
|
menuShown: false,
|
|
}
|
|
},
|
|
computed: {
|
|
selectedNames(): string[] {
|
|
return this.selectedItemKeys.map((key) => {
|
|
const product = this.items.find((p) => p.key === key)
|
|
return product ? product.name : ''
|
|
})
|
|
},
|
|
},
|
|
mounted() {
|
|
document.addEventListener('click', this.onClickDocument)
|
|
},
|
|
destroyed() {
|
|
document.removeEventListener('click', this.onClickDocument)
|
|
},
|
|
methods: {
|
|
onChange(event: any, key: string) {
|
|
const checked = event.target && event.target.checked
|
|
const updated = checked
|
|
? [...new Set([...this.selectedItemKeys, key])]
|
|
: this.selectedItemKeys.filter((k) => k !== key)
|
|
this.selectedItemKeys = updated
|
|
this.$emit('change', updated)
|
|
},
|
|
onClickDocument(event: MouseEvent) {
|
|
if (!this.$el.contains(event.target as Node)) {
|
|
this.menuShown = false
|
|
}
|
|
},
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.select {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
.button {
|
|
margin: 0 auto;
|
|
background-color: var(--color-secondary-dark);
|
|
border-radius: 2rem;
|
|
padding: 0 1.25rem 0 1.75rem;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 1rem;
|
|
color: var(--color-primary-light);
|
|
text-align: left;
|
|
height: 3rem;
|
|
width: 100%;
|
|
}
|
|
|
|
.text {
|
|
white-space: nowrap;
|
|
width: calc(100% - 2rem);
|
|
color: #ddd;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.menu {
|
|
z-index: 10;
|
|
position: absolute;
|
|
top: calc(100% + 1rem);
|
|
left: 0;
|
|
right: 0;
|
|
background-color: var(--color-secondary-dark);
|
|
border-radius: 4px;
|
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.5), 0 4px 10px rgba(0, 0, 0, 0.5);
|
|
text-align: left;
|
|
max-height: 24rem;
|
|
overflow-y: scroll;
|
|
}
|
|
|
|
.item {
|
|
display: block;
|
|
padding: 0.75rem 1rem;
|
|
color: #fff;
|
|
cursor: pointer;
|
|
padding-left: 3rem;
|
|
position: relative;
|
|
}
|
|
|
|
.item + .item {
|
|
border-top: 1px solid var(--color-secondary-light);
|
|
}
|
|
|
|
.item:before,
|
|
.item:after {
|
|
position: absolute;
|
|
content: '';
|
|
}
|
|
|
|
.item:before {
|
|
width: 1.25rem;
|
|
height: 1.25rem;
|
|
border-radius: 2px;
|
|
background-color: var(--color-secondary-light);
|
|
left: 1rem;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.item.selected:after {
|
|
width: 0.5rem;
|
|
height: 0.75rem;
|
|
border-right: 4px solid #00c4e4;
|
|
border-bottom: 4px solid #00c4e4;
|
|
transform: rotate(45deg) translateY(-50%);
|
|
left: 1.125rem;
|
|
top: calc(50% - 0.175rem);
|
|
}
|
|
|
|
.item__checkbox {
|
|
display: none;
|
|
}
|
|
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.2s, transform 0.2s ease-out;
|
|
}
|
|
.fade-enter,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(-10px);
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
.button {
|
|
width: 85vw;
|
|
}
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.button {
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.button:hover {
|
|
background-color: var(--color-secondary);
|
|
}
|
|
|
|
.item:before {
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.item:hover:before {
|
|
background-color: var(--color-secondary);
|
|
}
|
|
}
|
|
</style>
|