- Add focus outline and ring styles to SelectTrigger - Conditionally apply text color based on modelValue - Simplify SelectItem template structure
83 lines
2.1 KiB
Vue
83 lines
2.1 KiB
Vue
<script setup lang="ts">
|
|
import { SelectRoot } from 'radix-vue'
|
|
import {
|
|
SelectContent,
|
|
SelectGroup,
|
|
SelectItem,
|
|
SelectLabel,
|
|
SelectSeparator,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '~/components/pub/ui/select'
|
|
import { cn } from '~/lib/utils'
|
|
|
|
interface Item {
|
|
value: string
|
|
label: string
|
|
code?: string
|
|
}
|
|
|
|
const props = defineProps<{
|
|
modelValue?: string
|
|
items: Item[]
|
|
placeholder?: string
|
|
label?: string
|
|
separator?: boolean
|
|
class?: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: string]
|
|
}>()
|
|
|
|
// Sort items with selected item first, then alphabetically
|
|
const sortedItems = computed(() => {
|
|
const itemsWithSelection = props.items.map(item => ({
|
|
...item,
|
|
isSelected: item.value === props.modelValue,
|
|
}))
|
|
|
|
return itemsWithSelection.sort((a, b) => {
|
|
// Selected item always comes first
|
|
if (a.isSelected && !b.isSelected) return -1
|
|
if (!a.isSelected && b.isSelected) return 1
|
|
|
|
// If neither or both are selected, sort by label alphabetically
|
|
return a.label.localeCompare(b.label)
|
|
})
|
|
})
|
|
|
|
function onValueChange(value: string) {
|
|
emit('update:modelValue', value)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<SelectRoot :model-value="modelValue" @update:model-value="onValueChange">
|
|
<SelectTrigger :class="cn('w-full focus:outline-none focus:ring-1 focus:ring-black bg-white', props.class)">
|
|
<SelectValue :placeholder="placeholder || 'Pilih item'" :class="cn(
|
|
props.modelValue ? 'text-black' : 'text-muted-foreground',
|
|
)" />
|
|
</SelectTrigger>
|
|
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectLabel v-if="label">
|
|
{{ label }}
|
|
</SelectLabel>
|
|
|
|
<SelectItem v-for="item in sortedItems" :key="item.value" :value="item.value" class="cursor-pointer">
|
|
<div class="flex items-center justify-between w-full">
|
|
<span>{{ item.label }}</span>
|
|
<span v-if="item.code" class="text-xs text-muted-foreground ml-2">
|
|
{{ item.code }}
|
|
</span>
|
|
</div>
|
|
</SelectItem>
|
|
|
|
<SelectSeparator v-if="separator" />
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</SelectRoot>
|
|
</template>
|