feat(division): wip tree select component
feat(division): update division list components and add mock api - Replace patient API endpoint with division mock endpoint - Simplify table columns and headers for division list - Add mock API endpoint for division list with tree/flat format feat(select-tree): add collapsible tree select component with lazy loading Implement a tree select component with collapsible sections and lazy loading of child items. Includes: - Collapsible component wrappers for Vue - Command component wrappers for combobox functionality - Tree select item component with loading states - Example implementation in dev page todo: - scroll on overflow - long text truncate possibly with tooltip - more than > 5 depth of child - mutate the children lazy - integration backend for search based text and return keys feat(select-tree): add command-item component for tree selection adjust hover bg-accent (remove state on-highlighted at styling) to avoid conflict on global component refactor(select-tree): extract TreeItem interface to shared type file Move TreeItem interface to a dedicated type file for better code organization and reusability. Update components to import the interface and add styling improvements to the tree-select component. adjust text size for tree to sm refactor(select-tree): rename tree-select-item to leaf and improve component - Rename component to better reflect its purpose as a leaf node - Improve UI with better spacing and hover states - Simplify toggle logic using v-model - Add checkmark icon for selected items checkpoint wip
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
<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)
|
||||
|
||||
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'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover v-model:open="open">
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="outline" role="combobox" class="w-full justify-between bg-white border-1 border-gray-400">
|
||||
<span
|
||||
class="font-normal text-muted-foreground" :class="cn(
|
||||
'font-normal',
|
||||
!modelValue && 'text-muted-foreground',
|
||||
modelValue && 'text-black',
|
||||
)"
|
||||
>
|
||||
{{ selectedLabel }}
|
||||
</span>
|
||||
<ChevronsUpDown class="w-4 h-4 ml-2 opacity-50 shrink-0" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="min-w-full max-w-[350px] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Cari item..." />
|
||||
<CommandEmpty>Item tidak ditemukan.</CommandEmpty>
|
||||
<CommandList class="max-h-[300px] overflow-x-auto overflow-y-auto">
|
||||
<CommandGroup>
|
||||
<TreeView
|
||||
:data="data"
|
||||
:selected-value="modelValue"
|
||||
:on-fetch-children="onFetchChildren"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
Reference in New Issue
Block a user