2021-01-06 23:36:31 +01:00
|
|
|
<template>
|
|
|
|
<div
|
|
|
|
class="multiselect"
|
|
|
|
:class="{'has-search-results': searchResultsVisible}"
|
|
|
|
ref="multiselectRoot"
|
2021-04-18 16:50:12 +02:00
|
|
|
tabindex="-1"
|
|
|
|
@focus="focus"
|
2021-01-06 23:36:31 +01:00
|
|
|
>
|
2021-01-14 22:19:28 +01:00
|
|
|
<div class="control" :class="{'is-loading': loading || localLoading}">
|
2021-11-09 20:07:06 +01:00
|
|
|
<div
|
|
|
|
class="input-wrapper input"
|
|
|
|
:class="{'has-multiple': hasMultiple}">
|
2021-01-14 22:19:28 +01:00
|
|
|
<template v-if="Array.isArray(internalValue)">
|
|
|
|
<template v-for="(item, key) in internalValue">
|
|
|
|
<slot name="tag" :item="item">
|
2021-01-06 23:36:31 +01:00
|
|
|
<span :key="`item${key}`" class="tag ml-2 mt-2">
|
|
|
|
{{ label !== '' ? item[label] : item }}
|
|
|
|
<a @click="() => remove(item)" class="delete is-small"></a>
|
|
|
|
</span>
|
2021-01-14 22:19:28 +01:00
|
|
|
</slot>
|
|
|
|
</template>
|
2021-01-06 23:36:31 +01:00
|
|
|
</template>
|
2021-01-14 22:19:28 +01:00
|
|
|
|
2021-01-06 23:36:31 +01:00
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
class="input"
|
|
|
|
v-model="query"
|
|
|
|
@keyup="search"
|
|
|
|
@keyup.enter.exact.prevent="() => createOrSelectOnEnter()"
|
|
|
|
:placeholder="placeholder"
|
2021-06-03 22:58:47 +02:00
|
|
|
@keydown.down.exact.prevent="() => preSelect(0)"
|
2021-01-06 23:36:31 +01:00
|
|
|
ref="searchInput"
|
2021-06-24 15:22:48 +02:00
|
|
|
@focus="handleFocus"
|
2021-01-06 23:36:31 +01:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<transition name="fade">
|
2021-05-30 20:30:08 +02:00
|
|
|
<div class="search-results" :class="{'search-results-inline': inline}" v-if="searchResultsVisible">
|
2021-01-06 23:36:31 +01:00
|
|
|
<button
|
2021-01-17 18:57:57 +01:00
|
|
|
class="is-fullwidth"
|
2021-06-03 22:58:47 +02:00
|
|
|
v-for="(data, key) in filteredSearchResults"
|
|
|
|
:key="key"
|
|
|
|
:ref="`result-${key}`"
|
|
|
|
@keydown.up.prevent="() => preSelect(key - 1)"
|
|
|
|
@keydown.down.prevent="() => preSelect(key + 1)"
|
|
|
|
@click.prevent.stop="() => select(data)"
|
2021-01-06 23:36:31 +01:00
|
|
|
>
|
|
|
|
<span>
|
2021-06-03 22:58:47 +02:00
|
|
|
<slot name="searchResult" :option="data">
|
|
|
|
<span class="search-result">{{ label !== '' ? data[label] : data }}</span>
|
2021-01-06 23:36:31 +01:00
|
|
|
</slot>
|
|
|
|
</span>
|
|
|
|
<span class="hint-text">
|
2021-06-03 22:58:47 +02:00
|
|
|
{{ selectPlaceholder }}
|
2021-01-06 23:36:31 +01:00
|
|
|
</span>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<button
|
2021-06-03 22:58:47 +02:00
|
|
|
v-if="creatableAvailable"
|
2021-01-17 18:57:57 +01:00
|
|
|
class="is-fullwidth"
|
2021-06-03 22:58:47 +02:00
|
|
|
:ref="`result-${filteredSearchResults.length}`"
|
|
|
|
@keydown.up.prevent="() => preSelect(filteredSearchResults.length - 1)"
|
|
|
|
@keydown.down.prevent="() => preSelect(filteredSearchResults.length + 1)"
|
|
|
|
@keyup.enter.prevent="create"
|
|
|
|
@click.prevent.stop="create"
|
2021-01-06 23:36:31 +01:00
|
|
|
>
|
|
|
|
<span>
|
2021-06-03 22:58:47 +02:00
|
|
|
<slot name="searchResult" :option="query">
|
|
|
|
<span class="search-result">
|
|
|
|
{{ query }}
|
|
|
|
</span>
|
2021-01-06 23:36:31 +01:00
|
|
|
</slot>
|
|
|
|
</span>
|
|
|
|
<span class="hint-text">
|
2021-06-03 22:58:47 +02:00
|
|
|
{{ createPlaceholder }}
|
2021-01-06 23:36:31 +01:00
|
|
|
</span>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</transition>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2021-11-09 20:07:06 +01:00
|
|
|
import {i18n} from '@/i18n'
|
2021-01-06 23:36:31 +01:00
|
|
|
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'multiselect',
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
query: '',
|
|
|
|
searchTimeout: null,
|
|
|
|
localLoading: false,
|
|
|
|
showSearchResults: false,
|
|
|
|
internalValue: null,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
props: {
|
|
|
|
// When true, shows a loading spinner
|
|
|
|
loading: {
|
|
|
|
type: Boolean,
|
|
|
|
default() {
|
|
|
|
return false
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// The placeholder of the search input
|
|
|
|
placeholder: {
|
|
|
|
type: String,
|
|
|
|
default() {
|
|
|
|
return ''
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// The search results where the @search listener needs to put the results into
|
|
|
|
searchResults: {
|
|
|
|
type: Array,
|
|
|
|
default() {
|
|
|
|
return []
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// The name of the property of the searched object to show the user.
|
|
|
|
// If empty the component will show all raw data of an entry.
|
|
|
|
label: {
|
|
|
|
type: String,
|
|
|
|
default() {
|
|
|
|
return ''
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// The object with the value, updated every time an entry is selected.
|
2021-08-23 21:18:12 +02:00
|
|
|
modelValue: {
|
2021-01-06 23:36:31 +01:00
|
|
|
default() {
|
|
|
|
return null
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
|
|
|
creatable: {
|
|
|
|
type: Boolean,
|
2021-11-09 20:07:06 +01:00
|
|
|
default: false,
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// The text shown next to the new value option.
|
|
|
|
createPlaceholder: {
|
|
|
|
type: String,
|
|
|
|
default() {
|
2021-08-20 15:38:16 +02:00
|
|
|
return i18n.global.t('input.multiselect.createPlaceholder')
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// The text shown next to an option.
|
|
|
|
selectPlaceholder: {
|
|
|
|
type: String,
|
|
|
|
default() {
|
2021-08-20 15:38:16 +02:00
|
|
|
return i18n.global.t('input.multiselect.selectPlaceholder')
|
2021-07-17 23:21:46 +02:00
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
// If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
|
|
|
|
multiple: {
|
|
|
|
type: Boolean,
|
2021-11-09 20:07:06 +01:00
|
|
|
default: false,
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
2021-05-30 20:30:08 +02:00
|
|
|
// If true, displays the search results inline instead of using a dropdown.
|
|
|
|
inline: {
|
|
|
|
type: Boolean,
|
2021-11-09 20:07:06 +01:00
|
|
|
default: false,
|
2021-05-30 20:30:08 +02:00
|
|
|
},
|
|
|
|
// If true, shows search results when no query is specified.
|
|
|
|
showEmpty: {
|
|
|
|
type: Boolean,
|
2021-11-09 20:07:06 +01:00
|
|
|
default: true,
|
2021-05-30 20:30:08 +02:00
|
|
|
},
|
|
|
|
// The delay in ms after which the search event will be fired. Used to avoid hitting the network on every keystroke.
|
|
|
|
searchDelay: {
|
|
|
|
type: Number,
|
|
|
|
default() {
|
|
|
|
return 200
|
|
|
|
},
|
|
|
|
},
|
2021-11-09 20:07:06 +01:00
|
|
|
closeAfterSelect: {
|
|
|
|
type: Boolean,
|
|
|
|
default: true,
|
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
2021-11-09 20:07:06 +01:00
|
|
|
|
2021-08-23 21:18:12 +02:00
|
|
|
/**
|
|
|
|
* Available events:
|
|
|
|
* @search: Triggered every time the search query input changes
|
|
|
|
* @select: Triggered every time an option from the search results is selected. Also triggers a change in v-model.
|
|
|
|
* @create: If nothing or no exact match was found and `creatable` is true, this event is triggered with the current value of the search query.
|
|
|
|
* @remove: If `multiple` is enabled, this will be fired every time an item is removed from the array of selected items.
|
|
|
|
*/
|
|
|
|
emits: ['update:modelValue', 'search', 'select', 'create', 'remove'],
|
|
|
|
|
2021-01-06 23:36:31 +01:00
|
|
|
mounted() {
|
|
|
|
document.addEventListener('click', this.hideSearchResultsHandler)
|
|
|
|
},
|
2021-08-19 21:36:09 +02:00
|
|
|
beforeUnmount() {
|
2021-01-06 23:36:31 +01:00
|
|
|
document.removeEventListener('click', this.hideSearchResultsHandler)
|
|
|
|
},
|
|
|
|
watch: {
|
2021-08-23 21:18:12 +02:00
|
|
|
modelValue: {
|
2021-09-08 11:59:38 +02:00
|
|
|
handler(value) {
|
|
|
|
this.setSelectedObject(value)
|
|
|
|
},
|
|
|
|
immediate: true,
|
2021-10-07 15:49:03 +02:00
|
|
|
deep: true,
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
searchResultsVisible() {
|
2021-06-03 22:58:47 +02:00
|
|
|
if (this.query === '' && !this.showEmpty) {
|
2021-05-30 20:30:08 +02:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-01-06 23:36:31 +01:00
|
|
|
return this.showSearchResults && (
|
2021-06-03 22:58:47 +02:00
|
|
|
(this.filteredSearchResults.length > 0) ||
|
|
|
|
(this.creatable && this.query !== '')
|
|
|
|
)
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
creatableAvailable() {
|
|
|
|
return this.creatable && this.query !== '' && !this.filteredSearchResults.some(elem => {
|
|
|
|
// Don't make create available if we have an exact match in our search results.
|
|
|
|
if (this.label !== '') {
|
|
|
|
return elem[this.label] === this.query
|
|
|
|
}
|
|
|
|
|
|
|
|
return elem === this.query
|
|
|
|
})
|
|
|
|
},
|
|
|
|
filteredSearchResults() {
|
2021-07-22 21:57:33 +02:00
|
|
|
if (this.multiple && this.internalValue !== null && Array.isArray(this.internalValue)) {
|
2021-01-06 23:36:31 +01:00
|
|
|
return this.searchResults.filter(item => !this.internalValue.some(e => e === item))
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.searchResults
|
|
|
|
},
|
2021-11-09 20:07:06 +01:00
|
|
|
hasMultiple() {
|
|
|
|
return this.multiple && Array.isArray(this.internalValue) && this.internalValue.length > 0
|
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
// Searching will be triggered with a 200ms delay to avoid searching on every keyup event.
|
|
|
|
search() {
|
2021-01-18 21:33:21 +01:00
|
|
|
|
|
|
|
// Updating the query with a binding does not work on mobile for some reason,
|
|
|
|
// getting the value manual does.
|
|
|
|
this.query = this.$refs.searchInput.value
|
2021-05-30 20:30:08 +02:00
|
|
|
|
2021-01-06 23:36:31 +01:00
|
|
|
if (this.searchTimeout !== null) {
|
|
|
|
clearTimeout(this.searchTimeout)
|
|
|
|
this.searchTimeout = null
|
|
|
|
}
|
|
|
|
|
|
|
|
this.localLoading = true
|
|
|
|
|
|
|
|
this.searchTimeout = setTimeout(() => {
|
|
|
|
this.$emit('search', this.query)
|
|
|
|
setTimeout(() => {
|
|
|
|
this.localLoading = false
|
|
|
|
}, 100) // The duration of the loading timeout of the services
|
|
|
|
this.showSearchResults = true
|
2021-05-30 20:30:08 +02:00
|
|
|
}, this.searchDelay)
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
hideSearchResultsHandler(e) {
|
|
|
|
closeWhenClickedOutside(e, this.$refs.multiselectRoot, this.closeSearchResults)
|
|
|
|
},
|
|
|
|
closeSearchResults() {
|
|
|
|
this.showSearchResults = false
|
|
|
|
},
|
2021-06-24 15:22:48 +02:00
|
|
|
handleFocus() {
|
|
|
|
// We need the timeout to avoid the hideSearchResultsHandler hiding the search results right after the input
|
|
|
|
// is focused. That would lead to flickering pre-loaded search results and hiding them right after showing.
|
|
|
|
setTimeout(() => {
|
|
|
|
this.showSearchResults = true
|
|
|
|
}, 10)
|
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
select(object) {
|
|
|
|
if (this.multiple) {
|
|
|
|
if (this.internalValue === null) {
|
|
|
|
this.internalValue = []
|
|
|
|
}
|
|
|
|
|
|
|
|
this.internalValue.push(object)
|
|
|
|
} else {
|
|
|
|
this.internalValue = object
|
|
|
|
}
|
|
|
|
|
2021-08-23 21:18:12 +02:00
|
|
|
this.$emit('update:modelValue', this.internalValue)
|
2021-01-06 23:36:31 +01:00
|
|
|
this.$emit('select', object)
|
|
|
|
this.setSelectedObject(object)
|
2021-11-09 20:07:06 +01:00
|
|
|
if (this.closeAfterSelect && this.filteredSearchResults.length > 0 && !this.creatableAvailable) {
|
|
|
|
this.closeSearchResults()
|
|
|
|
}
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
setSelectedObject(object, resetOnly = false) {
|
2021-08-19 21:35:38 +02:00
|
|
|
this.internalValue = object
|
2021-01-06 23:36:31 +01:00
|
|
|
|
|
|
|
// We assume we're getting an array when multiple is enabled and can therefore leave the query
|
|
|
|
// value etc as it is
|
|
|
|
if (this.multiple) {
|
|
|
|
this.query = ''
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object === null) {
|
|
|
|
this.query = ''
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resetOnly) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.query = this.label !== '' ? object[this.label] : object
|
|
|
|
},
|
2021-06-03 22:58:47 +02:00
|
|
|
preSelect(index) {
|
|
|
|
if (index < 0) {
|
2021-01-06 23:36:31 +01:00
|
|
|
this.$refs.searchInput.focus()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const elems = this.$refs[`result-${index}`]
|
|
|
|
if (typeof elems === 'undefined' || elems.length === 0) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Array.isArray(elems)) {
|
|
|
|
elems[0].focus()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
elems.focus()
|
|
|
|
},
|
|
|
|
create() {
|
|
|
|
if (this.query === '') {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$emit('create', this.query)
|
|
|
|
this.setSelectedObject(this.query, true)
|
|
|
|
this.closeSearchResults()
|
|
|
|
},
|
|
|
|
createOrSelectOnEnter() {
|
|
|
|
|
|
|
|
if (!this.creatableAvailable && this.searchResults.length === 1) {
|
|
|
|
this.select(this.searchResults[0])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.creatableAvailable) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.create()
|
|
|
|
},
|
|
|
|
remove(item) {
|
|
|
|
for (const ind in this.internalValue) {
|
|
|
|
if (this.internalValue[ind] === item) {
|
|
|
|
this.internalValue.splice(ind, 1)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-23 21:18:12 +02:00
|
|
|
this.$emit('update:modelValue', this.internalValue)
|
2021-01-06 23:36:31 +01:00
|
|
|
this.$emit('remove', item)
|
|
|
|
},
|
2021-04-18 16:50:12 +02:00
|
|
|
focus() {
|
|
|
|
this.$refs.searchInput.focus()
|
|
|
|
},
|
2021-01-06 23:36:31 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
</script>
|
2021-10-18 14:19:24 +02:00
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.multiselect {
|
|
|
|
width: 100%;
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
.control.is-loading::after {
|
|
|
|
top: .75rem;
|
|
|
|
}
|
|
|
|
|
|
|
|
&.has-search-results .input-wrapper {
|
|
|
|
border-radius: $radius $radius 0 0;
|
2021-11-22 22:12:54 +01:00
|
|
|
border-color: var(--primary) !important;
|
|
|
|
background: var(--white) !important;
|
2021-10-18 14:19:24 +02:00
|
|
|
|
|
|
|
&, &:focus-within {
|
2021-11-22 22:12:54 +01:00
|
|
|
border-bottom-color: var(--grey-200) !important;
|
2021-10-18 14:19:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.input-wrapper {
|
|
|
|
padding: 0;
|
2021-11-22 22:12:54 +01:00
|
|
|
background: var(--white) !important;
|
|
|
|
border-color: var(--grey-200) !important;
|
2021-10-18 14:19:24 +02:00
|
|
|
flex-wrap: wrap;
|
|
|
|
height: auto;
|
|
|
|
|
|
|
|
&:hover {
|
2021-11-22 22:12:54 +01:00
|
|
|
border-color: var(--grey-300) !important;
|
2021-10-18 14:19:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.input {
|
|
|
|
display: flex;
|
|
|
|
max-width: 100%;
|
|
|
|
width: 100%;
|
|
|
|
align-items: center;
|
|
|
|
border: none !important;
|
|
|
|
background: transparent;
|
|
|
|
height: auto;
|
|
|
|
|
|
|
|
&::placeholder {
|
|
|
|
font-style: normal !important;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&.has-multiple .input {
|
|
|
|
max-width: 250px;
|
|
|
|
|
|
|
|
input {
|
|
|
|
padding-left: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:focus-within {
|
2021-11-22 22:12:54 +01:00
|
|
|
border-color: var(--primary) !important;
|
|
|
|
background: var(--white) !important;
|
2021-10-18 14:19:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.loader {
|
|
|
|
margin: 0 .5rem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.search-results {
|
2021-11-22 22:12:54 +01:00
|
|
|
background: var(--white);
|
2021-10-18 14:19:24 +02:00
|
|
|
border-radius: 0 0 $radius $radius;
|
2021-11-22 22:12:54 +01:00
|
|
|
border: 1px solid var(--primary);
|
2021-10-18 14:19:24 +02:00
|
|
|
border-top: none;
|
|
|
|
|
|
|
|
max-height: 50vh;
|
|
|
|
overflow-x: auto;
|
|
|
|
position: absolute;
|
|
|
|
z-index: 100;
|
|
|
|
max-width: 100%;
|
|
|
|
min-width: 100%;
|
|
|
|
|
|
|
|
&-inline {
|
|
|
|
position: static;
|
|
|
|
}
|
|
|
|
|
|
|
|
button {
|
|
|
|
background: transparent;
|
|
|
|
display: block;
|
|
|
|
text-align: left;
|
|
|
|
box-shadow: none;
|
|
|
|
border-radius: 0;
|
|
|
|
text-transform: none;
|
|
|
|
font-family: $family-sans-serif;
|
|
|
|
font-weight: normal;
|
|
|
|
padding: .5rem 0;
|
|
|
|
border: none;
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
.search-result {
|
|
|
|
white-space: nowrap;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
overflow: hidden;
|
|
|
|
padding: .5rem .75rem;
|
|
|
|
}
|
|
|
|
|
|
|
|
.hint-text {
|
|
|
|
font-size: .75rem;
|
|
|
|
color: transparent;
|
|
|
|
transition: color $transition;
|
|
|
|
padding: 0 .5rem;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:focus, &:hover {
|
2021-11-22 22:12:54 +01:00
|
|
|
background: var(--grey-100);
|
2021-10-18 14:19:24 +02:00
|
|
|
box-shadow: none !important;
|
|
|
|
|
|
|
|
.hint-text {
|
2021-11-22 22:12:54 +01:00
|
|
|
color: var(--text);
|
2021-10-18 14:19:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:active {
|
2021-11-22 22:12:54 +01:00
|
|
|
background: var(--grey-200);
|
2021-10-18 14:19:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|