dev: hotfix, moved combobox and datepicker
This commit is contained in:
No files matched your search
@@ -0,0 +1,149 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import { type Item } from './index'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string
|
||||
modelValue?: string
|
||||
items: Item[]
|
||||
placeholder?: string
|
||||
searchPlaceholder?: string
|
||||
emptyMessage?: string
|
||||
class?: string
|
||||
isDisabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
}>()
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
const selectedItem = computed(() => props.items.find((item) => item.value === props.modelValue))
|
||||
|
||||
const displayText = computed(() => {
|
||||
console.log(selectedItem);
|
||||
if (selectedItem.value?.label) {
|
||||
return selectedItem.value.label
|
||||
}
|
||||
return props.placeholder || 'Pilih item'
|
||||
})
|
||||
|
||||
watch(props, () => {
|
||||
console.log(props.modelValue);
|
||||
})
|
||||
|
||||
const searchableItems = computed(() => {
|
||||
const itemsWithSearch = props.items.map((item) => ({
|
||||
...item,
|
||||
searchValue: `${item.code || ''} ${item.label}`.trim(),
|
||||
isSelected: item.value === props.modelValue,
|
||||
}))
|
||||
|
||||
return itemsWithSearch.sort((a, b) => {
|
||||
const aPriority = a.priority ?? 0
|
||||
const bPriority = b.priority ?? 0
|
||||
if (aPriority !== bPriority) {
|
||||
return bPriority - aPriority
|
||||
}
|
||||
|
||||
if (a.isSelected && !b.isSelected) return -1
|
||||
if (!a.isSelected && b.isSelected) return 1
|
||||
|
||||
return a.label.localeCompare(b.label)
|
||||
})
|
||||
})
|
||||
|
||||
function onSelect(item: Item) {
|
||||
emit('update:modelValue', item.value)
|
||||
open.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover v-model:open="open">
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
:id="props.id"
|
||||
:disabled="props.isDisabled"
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
:aria-expanded="open"
|
||||
:aria-controls="`${props.id}-list`"
|
||||
:aria-describedby="`${props.id}-search`"
|
||||
:class="
|
||||
cn(
|
||||
'w-full justify-between border text-sm font-normal rounded-md px-3 py-2 focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white',
|
||||
{
|
||||
'cursor-not-allowed bg-gray-100 opacity-50 border-gray-300 text-gray-500': props.isDisabled,
|
||||
'bg-white text-black dark:bg-gray-800 dark:text-white dark:border-gray-600 border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700': !props.isDisabled,
|
||||
'text-gray-400 dark:text-gray-500': !modelValue && !props.isDisabled,
|
||||
},
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ displayText }}
|
||||
<Icon
|
||||
name="i-lucide-chevrons-up-down"
|
||||
:class="cn('ml-2 h-4 w-4 shrink-0', {
|
||||
'opacity-30': props.isDisabled,
|
||||
'opacity-50 text-gray-500 dark:text-gray-300': !props.isDisabled
|
||||
})"
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-[var(--radix-popover-trigger-width)] p-0 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<Command class="bg-white dark:bg-gray-800">
|
||||
<CommandInput
|
||||
:id="`${props.id}-search`"
|
||||
class="h-9 bg-white dark:bg-gray-800 text-black dark:text-white border-0 border-b border-gray-200 dark:border-gray-700 focus:ring-0"
|
||||
:placeholder="searchPlaceholder || 'Cari...'"
|
||||
:aria-label="`Cari ${displayText}`"
|
||||
/>
|
||||
<CommandEmpty class="text-gray-500 dark:text-gray-400 py-6 text-center text-sm">
|
||||
{{ emptyMessage || 'Item tidak ditemukan.' }}
|
||||
</CommandEmpty>
|
||||
<CommandList
|
||||
:id="`${props.id}-list`"
|
||||
role="listbox"
|
||||
class="max-h-60 overflow-auto"
|
||||
>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
v-for="item in searchableItems"
|
||||
:key="item.value"
|
||||
:value="item.searchValue"
|
||||
:class="
|
||||
cn(
|
||||
'flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 text-sm',
|
||||
'focus:outline-none text-black dark:text-white',
|
||||
'hover:bg-primary hover:text-white focus:bg-primary focus:text-white',
|
||||
'data-[highlighted]:bg-primary data-[highlighted]:text-white',
|
||||
)
|
||||
"
|
||||
@select="onSelect(item)"
|
||||
>
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<span>{{ item.label }}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
v-if="item.code"
|
||||
class="text-xs text-muted-foreground"
|
||||
>
|
||||
{{ item.code }}
|
||||
</span>
|
||||
<Icon
|
||||
name="i-lucide-check"
|
||||
:class="cn('h-4 w-4', modelValue === item.value ? 'opacity-100' : 'opacity-0')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
export interface Item {
|
||||
value: string
|
||||
label: string
|
||||
code?: string
|
||||
priority?: number
|
||||
}
|
||||
|
||||
export function recStrToItem(input: Record<string, string>): Item[] {
|
||||
console.log(input)
|
||||
const items: Item[] = []
|
||||
let idx = 0;
|
||||
for (const key in input) {
|
||||
if (input.hasOwnProperty(key)) {
|
||||
const value = input[key]
|
||||
items.push({
|
||||
value: key || ('unknown-' + idx),
|
||||
label: input[key] || ('unknown-' + idx),
|
||||
})
|
||||
}
|
||||
idx++
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
export { default as Combobox } from './combobox.vue'
|
||||
Reference in New Issue
Block a user