dev: hotfix, moved combobox and datepicker

This commit is contained in:
2025-10-10 23:58:44 +07:00
parent f94b6d273a
commit 1a3edd5a1e
25 changed files with 58 additions and 33 deletions
-149
View File
@@ -1,149 +0,0 @@
<script setup lang="ts">
import { cn } from '~/lib/utils'
interface Item {
value: string | number
label: string
code?: string
priority?: number
}
const props = defineProps<{
id: string
modelValue?: string | number
items: Item[]
placeholder?: string
searchPlaceholder?: string
emptyMessage?: string
class?: string
isDisabled?: boolean
}>()
const emit = defineEmits<{
'update:modelValue': [value: string | number]
}>()
const open = ref(false)
const selectedItem = computed(() => props.items.find((item) => item.value === props.modelValue))
const displayText = computed(() => {
if (selectedItem.value?.label) {
return selectedItem.value.label
}
return props.placeholder || 'Pilih item'
})
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>
@@ -1,57 +0,0 @@
<script setup lang="ts">
// helpers
import { format, parseISO } from 'date-fns'
import { id as localeID } from 'date-fns/locale'
// components
import { Button } from '~/components/pub/ui/button'
import Calendar from '~/components/pub/ui/calendar/Calendar.vue'
import { Popover, PopoverContent, PopoverTrigger } from '~/components/pub/ui/popover'
const props = defineProps<{
placeholder?: string
modelValue?: Date | string | undefined
}>()
const emit = defineEmits<{
'update:modelValue': [value: Date | string | undefined]
}>()
const date = ref<Date | any>(undefined)
watch(date, (value) => {
const newValue = format(value, 'yyyy-MM-dd', { locale: localeID })
emit('update:modelValue', newValue)
})
onMounted(() => {
if (props.modelValue) {
const value = props.modelValue
if (value instanceof Date) {
date.value = value
} else if (typeof value === 'string' && value) {
date.value = parseISO(value)
} else {
date.value = undefined
}
}
})
</script>
<template>
<div class="flex flex-col space-y-2">
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="bg-white border-gray-400 font-normal text-right h-[40px] w-full">
<div class="flex justify-between items-center w-full">
<p v-if="date">{{ format(date, 'PPP', { locale: localeID }) }}</p>
<p v-else class="text-sm text-black text-opacity-50">{{ props.placeholder || 'Tanggal' }}</p>
<Icon name="i-lucide-calendar" class="h-5 w-5" />
</div>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<Calendar v-model="date" mode="single" locale="id" />
</PopoverContent>
</Popover>
</div>
</template>