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

View file

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