refactor: rework useSearch and ViewSearch to no longer use ViewBase and expect defined data

This commit is contained in:
axolotle 2024-08-11 17:55:23 +02:00
parent da02692d93
commit 1f1dac1792
2 changed files with 43 additions and 49 deletions

View file

@ -1,5 +1,5 @@
<script setup lang="ts" generic="T extends Obj | AnyTreeNode"> <script setup lang="ts" generic="T extends Obj | AnyTreeNode">
import { computed, type Component } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import type { AnyTreeNode } from '@/helpers/data/tree' import type { AnyTreeNode } from '@/helpers/data/tree'
@ -10,12 +10,10 @@ const props = withDefaults(
items?: T[] | null items?: T[] | null
itemsName: string | null itemsName: string | null
modelValue?: string modelValue?: string
skeleton?: string | Component
}>(), }>(),
{ {
items: undefined, items: undefined,
modelValue: undefined, modelValue: undefined,
skeleton: 'ListGroupSkeleton',
}, },
) )
@ -24,6 +22,7 @@ const slots = defineSlots<{
'top-bar-buttons': any 'top-bar-buttons': any
top: any top: any
'alert-message': any 'alert-message': any
'forced-default'?: any
default: any default: any
bot: any bot: any
skeleton: any skeleton: any
@ -47,11 +46,10 @@ const noItemsMessage = computed(() => {
</script> </script>
<template> <template>
<ViewBase :skeleton="skeleton"> <div>
<template v-if="slots['top-bar']" #top-bar> <slot v-if="slots['top-bar']" name="top-bar" />
<slot name="top-bar" /> <TopBar v-else>
</template> <template #group-left>
<template v-if="!slots['top-bar']" #top-bar-group-left>
<BInputGroup class="w-100"> <BInputGroup class="w-100">
<BInputGroupText> <BInputGroupText>
<YIcon iname="search" /> <YIcon iname="search" />
@ -60,36 +58,30 @@ const noItemsMessage = computed(() => {
<BFormInput <BFormInput
id="top-bar-search" id="top-bar-search"
v-model="model" v-model="model"
:placeholder="t('search.for', { items: t('items.' + itemsName, 2) })" :placeholder="
:disabled="!items" t('search.for', { items: t('items.' + itemsName, 2) })
"
:disabled="items === undefined"
/> />
</BInputGroup> </BInputGroup>
</template> </template>
<template v-if="!slots['top-bar']" #top-bar-group-right> <template #group-right>
<slot name="top-bar-buttons" /> <slot name="top-bar-buttons" />
</template> </template>
</TopBar>
<template #top>
<slot name="top" /> <slot name="top" />
</template>
<template #default> <slot name="forced-default">
<BAlert v-if="noItemsMessage" :model-value="true" variant="warning"> <BAlert v-if="noItemsMessage" :model-value="true" variant="warning">
<slot name="alert-message"> <slot name="alert-message">
<YIcon iname="exclamation-triangle" /> <YIcon iname="exclamation-triangle" />
{{ noItemsMessage }} {{ noItemsMessage }}
</slot> </slot>
</BAlert> </BAlert>
<slot v-else name="default" /> <slot v-else name="default" />
</template> </slot>
<template #bot>
<slot name="bot" /> <slot name="bot" />
</template> </div>
<template #skeleton>
<slot name="skeleton" />
</template>
</ViewBase>
</template> </template>

View file

@ -1,15 +1,19 @@
import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue' import type {
import { computed, ref, toValue, watch } from 'vue' ComputedRef,
MaybeRefOrGetter,
Ref,
WritableComputedRef,
} from 'vue'
import { computed, isRef, ref, toValue } from 'vue'
import type { AnyTreeNode, TreeRootNode } from '@/helpers/data/tree' import type { AnyTreeNode, TreeRootNode } from '@/helpers/data/tree'
// Returns `undefined` when there's no items and `null` when there's no match
export function useSearch< export function useSearch<
T extends any[] | TreeRootNode, T extends any[] | TreeRootNode,
V extends T extends (infer V)[] ? V : AnyTreeNode, V extends T extends (infer V)[] ? V : AnyTreeNode,
>( >(
items: items: MaybeRefOrGetter<T> | ComputedRef<T>,
| MaybeRefOrGetter<T | null | undefined>
| ComputedRef<T | null | undefined>,
filterFn: (search: string, item: V, index: number, arr: T) => boolean, filterFn: (search: string, item: V, index: number, arr: T) => boolean,
{ {
externalSearch, externalSearch,
@ -18,19 +22,17 @@ export function useSearch<
}: { }: {
filterAllFn?: (search: string, items: T) => boolean | undefined filterAllFn?: (search: string, items: T) => boolean | undefined
filterIfNoSearch?: boolean filterIfNoSearch?: boolean
externalSearch?: MaybeRefOrGetter<string> externalSearch?: Ref<string> | WritableComputedRef<string>
} = {}, } = {},
): [search: Ref<string>, filteredItems: ComputedRef<T | undefined | null>] { ): [search: Ref<string>, filteredItems: ComputedRef<T | undefined | null>] {
const search = ref(toValue(externalSearch) ?? '') const search = isRef(externalSearch)
watch( ? externalSearch
() => toValue(externalSearch), : ref(toValue(externalSearch) ?? '')
(s) => (search.value = s ?? ''),
)
const filteredItems = computed(() => { const filteredItems = computed(() => {
const items_ = toValue(items) const items_ = toValue(items)
const s = toValue(search.value).toLowerCase() const s = toValue(search.value).toLowerCase()
if (!items_) return undefined if (!items_.length) return undefined
if (filterAllFn) { if (filterAllFn) {
const returnAll = filterAllFn(s, items_) const returnAll = filterAllFn(s, items_)
if (returnAll !== undefined) { if (returnAll !== undefined) {