* fix: adjustment some schemas * fix(room): fixing integrate unit of room * feat(warehouse): modify form and integration * feat(counter): modify form and integration * feat(screen): add list, form and integration * feat(screen): add page for public screen * fix: add on reset state at list * fix: solve list of relation * feat(chamber): integrate form to api chamber * feat(bed): integrate form to api bed * fix: add searching function on list service * fix: rewrite style for dropdown and tree select * fix: add sort params * fix: add sort params on division + medicine * feat(division-position): layouting form + list * fix: add sort params for getValueList * chore: modify side menu style * chore: fix ui dashboard * feat(division-position): add content list * feat(division-position): add temporary page * feat(division-position): modify content and entry form
98 lines
2.9 KiB
Vue
98 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import type { TreeItem } from './type'
|
|
import { ChevronsUpDown } from 'lucide-vue-next'
|
|
import { cn } from '~/lib/utils'
|
|
import TreeView from './tree-view.vue'
|
|
|
|
const props = defineProps<{
|
|
data: TreeItem[]
|
|
onFetchChildren: (parentId: string) => Promise<void>
|
|
}>()
|
|
|
|
const modelValue = defineModel<string>()
|
|
const open = ref(false)
|
|
const searchValue = ref('')
|
|
|
|
function handleSelect(newVal: string) {
|
|
modelValue.value = newVal
|
|
open.value = false
|
|
}
|
|
|
|
function findLabel(value: string, items: TreeItem[]): string | undefined {
|
|
for (const item of items) {
|
|
if (item.value === value) return item.label
|
|
if (item.children) {
|
|
const found = findLabel(value, item.children)
|
|
if (found) return found
|
|
}
|
|
}
|
|
}
|
|
|
|
const selectedLabel = computed(() => {
|
|
return modelValue.value ? findLabel(modelValue.value, props.data) : '--- select item'
|
|
})
|
|
|
|
const filteredData = computed(() => {
|
|
if (!searchValue.value) return props.data
|
|
// recursive filter
|
|
function filterTree(items: TreeItem[]): TreeItem[] {
|
|
return items
|
|
.map((item) => {
|
|
const match = item.label.toLowerCase().includes(searchValue.value.toLowerCase())
|
|
let children: TreeItem[] | undefined = undefined
|
|
if (item.children) {
|
|
children = filterTree(item.children)
|
|
}
|
|
if (match || (children && children.length > 0)) {
|
|
return { ...item, children }
|
|
}
|
|
return null
|
|
})
|
|
.filter(Boolean) as TreeItem[]
|
|
}
|
|
return filterTree(props.data)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Popover v-model:open="open">
|
|
<PopoverTrigger as-child>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
class="w-full justify-between border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
|
>
|
|
<span
|
|
class="font-normal text-muted-foreground"
|
|
:class="cn('font-normal', !modelValue && 'text-muted-foreground', modelValue && 'text-black')"
|
|
>
|
|
{{ selectedLabel }}
|
|
</span>
|
|
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent
|
|
class="min-w-full max-w-[350px] border border-slate-200 bg-white p-0 text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
|
>
|
|
<Command>
|
|
<CommandInput
|
|
placeholder="Cari item..."
|
|
v-model="searchValue"
|
|
/>
|
|
<CommandEmpty>Item tidak ditemukan.</CommandEmpty>
|
|
<CommandList class="max-h-[300px] overflow-x-auto overflow-y-auto">
|
|
<CommandGroup>
|
|
<TreeView
|
|
:data="filteredData"
|
|
:selected-value="modelValue"
|
|
:on-fetch-children="onFetchChildren"
|
|
:level="0"
|
|
@select="handleSelect"
|
|
/>
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</template>
|