fix(settings): allow removing the default project via settings

This commit is contained in:
kolaente 2023-10-20 16:01:22 +02:00
parent 8992caadf9
commit aeed4b3a3b
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
2 changed files with 47 additions and 22 deletions

View File

@ -9,9 +9,9 @@
<div class="control" :class="{'is-loading': loading || localLoading}"> <div class="control" :class="{'is-loading': loading || localLoading}">
<div <div
class="input-wrapper input" class="input-wrapper input"
:class="{'has-multiple': hasMultiple}" :class="{'has-multiple': hasMultiple, 'has-removal-button': removalAvailable}"
> >
<slot <slot
v-if="Array.isArray(internalValue)" v-if="Array.isArray(internalValue)"
name="items" name="items"
:items="internalValue" :items="internalValue"
@ -19,14 +19,14 @@
> >
<template v-for="(item, key) in internalValue"> <template v-for="(item, key) in internalValue">
<slot name="tag" :item="item"> <slot name="tag" :item="item">
<span :key="`item${key}`" class="tag ml-2 mt-2"> <span :key="`item${key}`" class="tag ml-2 mt-2">
{{ label !== '' ? item[label] : item }} {{ label !== '' ? item[label] : item }}
<BaseButton @click="() => remove(item)" class="delete is-small"></BaseButton> <BaseButton @click="() => remove(item)" class="delete is-small"></BaseButton>
</span> </span>
</slot> </slot>
</template> </template>
</slot> </slot>
<input <input
type="text" type="text"
class="input" class="input"
@ -40,6 +40,13 @@
:autocomplete="autocompleteEnabled ? undefined : 'off'" :autocomplete="autocompleteEnabled ? undefined : 'off'"
:spellcheck="autocompleteEnabled ? undefined : 'false'" :spellcheck="autocompleteEnabled ? undefined : 'false'"
/> />
<BaseButton
v-if="removalAvailable"
class="removal-button"
@click="resetSelectedValue"
>
<icon icon="times"/>
</BaseButton>
</div> </div>
</div> </div>
@ -159,7 +166,7 @@ const props = defineProps({
createPlaceholder: { createPlaceholder: {
type: String, type: String,
default() { default() {
const {t} = useI18n({useScope: 'global'}) const {t} = useI18n({useScope: 'global'})
return t('input.multiselect.createPlaceholder') return t('input.multiselect.createPlaceholder')
}, },
}, },
@ -169,7 +176,7 @@ const props = defineProps({
selectPlaceholder: { selectPlaceholder: {
type: String, type: String,
default() { default() {
const {t} = useI18n({useScope: 'global'}) const {t} = useI18n({useScope: 'global'})
return t('input.multiselect.selectPlaceholder') return t('input.multiselect.selectPlaceholder')
}, },
}, },
@ -215,23 +222,23 @@ const props = defineProps({
}) })
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: null): void (e: 'update:modelValue', value: null): void
/** /**
* Triggered every time the search query input changes * Triggered every time the search query input changes
*/ */
(e: 'search', query: string): void (e: 'search', query: string): void
/** /**
* Triggered every time an option from the search results is selected. Also triggers a change in v-model. * Triggered every time an option from the search results is selected. Also triggers a change in v-model.
*/ */
(e: 'select', value: {[key: string]: any}): void (e: 'select', value: {[key: string]: any}): void
/** /**
* If nothing or no exact match was found and `creatable` is true, this event is triggered with the current value of the search query. * If nothing or no exact match was found and `creatable` is true, this event is triggered with the current value of the search query.
*/ */
(e: 'create', query: string): void (e: 'create', query: string): void
/** /**
* If `multiple` is enabled, this will be fired every time an item is removed from the array of selected items. * If `multiple` is enabled, this will be fired every time an item is removed from the array of selected items.
*/ */
(e: 'remove', value: null): void (e: 'remove', value: null): void
}>() }>()
const query = ref<string | {[key: string]: any}>('') const query = ref<string | {[key: string]: any}>('')
@ -269,8 +276,8 @@ const creatableAvailable = computed(() => {
const hasResult = filteredSearchResults.value.some((elem: any) => elementInResults(elem, props.label, query.value)) const hasResult = filteredSearchResults.value.some((elem: any) => elementInResults(elem, props.label, query.value))
const hasQueryAlreadyAdded = Array.isArray(internalValue.value) && internalValue.value.some(elem => elementInResults(elem, props.label, query.value)) const hasQueryAlreadyAdded = Array.isArray(internalValue.value) && internalValue.value.some(elem => elementInResults(elem, props.label, query.value))
return props.creatable return props.creatable
&& query.value !== '' && query.value !== ''
&& !(hasResult || hasQueryAlreadyAdded) && !(hasResult || hasQueryAlreadyAdded)
}) })
@ -287,7 +294,13 @@ const hasMultiple = computed(() => {
return props.multiple && Array.isArray(internalValue.value) && internalValue.value.length > 0 return props.multiple && Array.isArray(internalValue.value) && internalValue.value.length > 0
}) })
const removalAvailable = computed(() => !props.multiple && internalValue.value !== null && query.value !== '')
function resetSelectedValue() {
select(null)
}
const searchInput = ref<HTMLInputElement | null>(null) const searchInput = ref<HTMLInputElement | null>(null)
// Searching will be triggered with a 200ms delay to avoid searching on every keyup event. // Searching will be triggered with a 200ms delay to avoid searching on every keyup event.
function search() { function search() {
@ -312,6 +325,7 @@ function search() {
} }
const multiselectRoot = ref<HTMLElement | null>(null) const multiselectRoot = ref<HTMLElement | null>(null)
function hideSearchResultsHandler(e: MouseEvent) { function hideSearchResultsHandler(e: MouseEvent) {
closeWhenClickedOutside(e, multiselectRoot.value, closeSearchResults) closeWhenClickedOutside(e, multiselectRoot.value, closeSearchResults)
} }
@ -328,7 +342,7 @@ function handleFocus() {
}, 10) }, 10)
} }
function select(object: {[key: string]: any}) { function select(object: {[key: string]: any} | null) {
if (props.multiple) { if (props.multiple) {
if (internalValue.value === null) { if (internalValue.value === null) {
internalValue.value = [] internalValue.value = []
@ -370,6 +384,7 @@ function setSelectedObject(object: string | {[id: string]: any} | null, resetOnl
} }
const results = ref<(Element | ComponentPublicInstance)[]>([]) const results = ref<(Element | ComponentPublicInstance)[]>([])
function setResult(el: Element | ComponentPublicInstance | null, index: number) { function setResult(el: Element | ComponentPublicInstance | null, index: number) {
if (el === null) { if (el === null) {
delete results.value[index] delete results.value[index]
@ -416,7 +431,7 @@ function createOrSelectOnEnter() {
if (!creatableAvailable.value) { if (!creatableAvailable.value) {
// Check if there's an exact match for our search term // Check if there's an exact match for our search term
const exactMatch = filteredSearchResults.value.find((elem: any) => elementInResults(elem, props.label, query.value)) const exactMatch = filteredSearchResults.value.find((elem: any) => elementInResults(elem, props.label, query.value))
if(exactMatch) { if (exactMatch) {
select(exactMatch) select(exactMatch)
} }
@ -572,4 +587,14 @@ function focus() {
transition: color $transition; transition: color $transition;
padding-left: .5rem; padding-left: .5rem;
} }
.has-removal-button {
position: relative;
}
.removal-button {
position: absolute;
right: .5rem;
color: var(--danger);
}
</style> </style>

View File

@ -70,11 +70,11 @@ function findProjects(query: string) {
foundProjects.value = projectStore.searchProject(query) foundProjects.value = projectStore.searchProject(query)
} }
function select(l: IProject | null) { function select(p: IProject | null) {
if (l === null) { if (p === null) {
return Object.assign(project, {id: 0})
} }
Object.assign(project, l) Object.assign(project, p)
emit('update:modelValue', project) emit('update:modelValue', project)
} }
</script> </script>