trypost/resources/js/components/TimezoneCombobox.vue
Paulo Castellano 07ddedceae fix: resolve all TypeScript errors
- Fix Auth type: add currentWorkspace, workspaces, FlashData
- Fix User.id type: number → string (UUID)
- Fix trans() number params: wrap with String()
- Fix debounce NodeJS.Timeout → ReturnType<typeof setTimeout>
- Fix DatePicker name prop: required → optional
- Fix DatePicker emit typing
- Fix storeStep1 → storeRole import in onboarding/Role
- Fix duplicate id in AcceptInvite interface
- Fix formatDate → formatDateTime in ApiKeys
- Add @unovis/vue and @unovis/ts type declarations
- Install @unovis/vue @unovis/ts dev dependencies
- Add type casts for Inertia/Reka UI component props
2026-03-31 09:34:39 -03:00

131 lines
3.9 KiB
Vue

<script setup lang="ts">
import { IconCheck, IconChevronDown, IconSearch } from '@tabler/icons-vue';
import { trans } from 'laravel-vue-i18n';
import { FocusScope } from 'reka-ui';
import { ref, watchEffect } from 'vue';
import { Button } from '@/components/ui/button';
import {
Combobox,
ComboboxAnchor,
ComboboxEmpty,
ComboboxGroup,
ComboboxInput,
ComboboxItem,
ComboboxItemIndicator,
ComboboxList,
ComboboxTrigger,
} from '@/components/ui/combobox';
interface Timezone {
value: string;
label: string;
}
interface Props {
modelValue?: string | null;
timezones: Record<string, string>;
}
const props = defineProps<Props>();
const emit = defineEmits<{
'update:modelValue': [value: string | null];
}>();
// Common timezones to show first
const commonTimezoneIds = [
'America/New_York',
'America/Chicago',
'America/Denver',
'America/Los_Angeles',
'America/Sao_Paulo',
'America/Mexico_City',
'Europe/London',
'Europe/Paris',
'Europe/Berlin',
'Asia/Tokyo',
'Asia/Shanghai',
'Asia/Dubai',
'Australia/Sydney',
'Pacific/Auckland',
];
// Build timezone list from props
const timezones = Object.keys(props.timezones)
.sort((a, b) => {
const aIsCommon = commonTimezoneIds.includes(a);
const bIsCommon = commonTimezoneIds.includes(b);
if (aIsCommon && !bIsCommon) return -1;
if (!aIsCommon && bIsCommon) return 1;
return a.localeCompare(b);
})
.map((tz) => ({
value: tz,
label: tz.replace(/_/g, ' '),
}));
const selectedTimezone = ref<Timezone | undefined>();
watchEffect(() => {
selectedTimezone.value = timezones.find(
(tz) => tz.value === props.modelValue,
);
});
</script>
<template>
<FocusScope as-child>
<Combobox
:model-value="selectedTimezone"
@update:model-value="
(v: any) => {
selectedTimezone = v;
emit('update:modelValue', v?.value || null);
}
"
>
<ComboboxAnchor as-child>
<ComboboxTrigger as-child>
<Button
variant="outline"
class="w-full justify-between"
>
{{
selectedTimezone
? selectedTimezone.label
: trans('common.timezone.select')
}}
<IconChevronDown
class="ml-2 h-4 w-4 shrink-0 opacity-50"
/>
</Button>
</ComboboxTrigger>
</ComboboxAnchor>
<ComboboxList class="w-full">
<div class="relative">
<ComboboxInput :placeholder="trans('common.timezone.search')" />
<span
class="absolute inset-y-0 start-0 flex items-center justify-center px-3"
>
<IconSearch class="size-4 text-muted-foreground" />
</span>
</div>
<ComboboxEmpty>{{ $t('common.timezone.empty') }}</ComboboxEmpty>
<ComboboxGroup>
<ComboboxItem
v-for="tz in timezones"
:key="tz.value"
:value="tz"
>
<span class="min-w-0 flex-1 truncate">{{
tz.label
}}</span>
<ComboboxItemIndicator>
<IconCheck class="ml-auto h-4 w-4" />
</ComboboxItemIndicator>
</ComboboxItem>
</ComboboxGroup>
</ComboboxList>
</Combobox>
</FocusScope>
</template>