Files
KanbanGit/resources/js/components/projects/PropertySelectDropdown.vue
T

103 lines
3.5 KiB
Vue

<script setup lang="ts">
import { Check, ChevronDown } from 'lucide-vue-next';
import { computed } from 'vue';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import type {
PropertySelectDropdownEmits,
PropertySelectDropdownProps,
} from '@/types';
const props = withDefaults(defineProps<PropertySelectDropdownProps>(), {
disabled: false,
});
const emit = defineEmits<PropertySelectDropdownEmits>();
const normalizedValue = computed(() => String(props.modelValue ?? ''));
const selectedOption = computed(
() =>
props.options.find(
(option) => String(option.value) === normalizedValue.value,
) ?? null,
);
const selectOption = (value: number | string): void => {
if (
!props.editable ||
props.disabled ||
String(value) === normalizedValue.value
) {
return;
}
emit('update:modelValue', value);
emit('commit', value);
};
</script>
<template>
<DropdownMenu v-if="editable">
<DropdownMenuTrigger as-child>
<button
type="button"
class="focus-visible:ring-ring/50 data-[state=open]:bg-primary/[0.08] data-[state=open]:text-primary hover:bg-primary/[0.055] hover:text-primary group -mx-1 flex min-h-7 w-[calc(100%+0.5rem)] items-center gap-2 rounded px-1 text-left text-sm font-medium outline-none transition-colors focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-60"
:aria-label="`Select ${label}`"
:disabled="disabled"
>
<span class="min-w-0 flex-1 truncate">
{{ selectedOption?.label ?? 'Not set' }}
</span>
<ChevronDown
class="text-muted-foreground size-3.5 shrink-0 transition-transform group-data-[state=open]:rotate-180"
/>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
:side-offset="6"
class="w-(--reka-dropdown-menu-trigger-width) min-w-52 rounded p-1"
>
<DropdownMenuItem
v-for="option in options"
:key="String(option.value)"
class="hover:bg-primary/[0.04] focus:bg-primary/[0.08] cursor-pointer gap-2.5 rounded px-2 py-2"
:class="
normalizedValue === String(option.value)
? 'bg-primary/[0.055] text-primary'
: ''
"
@select="selectOption(option.value)"
>
<span class="min-w-0 flex-1">
<span class="block truncate font-medium">
{{ option.label }}
</span>
<span
v-if="option.description"
class="text-muted-foreground block truncate text-[11px]"
>
{{ option.description }}
</span>
</span>
<Check
v-if="normalizedValue === String(option.value)"
class="text-primary size-4 shrink-0"
/>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<div
v-else
class="flex min-h-7 min-w-0 items-center gap-2 text-sm font-medium"
>
<span class="truncate">{{ selectedOption?.label ?? 'Not set' }}</span>
</div>
</template>