105 lines
2.3 KiB
Svelte
105 lines
2.3 KiB
Svelte
<script lang="ts" module>
|
|
const getTimeZonePart = (
|
|
timeZone: string,
|
|
timeZoneName: Intl.DateTimeFormatOptions['timeZoneName']
|
|
) => {
|
|
return new Intl.DateTimeFormat('en', {
|
|
timeZone,
|
|
timeZoneName
|
|
})
|
|
.formatToParts()
|
|
.find((part) => part.type === 'timeZoneName')?.value;
|
|
};
|
|
|
|
// wbr takes a string and adds a <wbr> tag after each forward slash
|
|
const wbr = (str: string): string => {
|
|
return str.replace(/\//g, '/<wbr />');
|
|
};
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import ExpandableCombobox, { type ComboboxItem } from './Combobox.svelte';
|
|
|
|
let {
|
|
label,
|
|
name,
|
|
value = $bindable(''),
|
|
invalidMessage,
|
|
required
|
|
}: {
|
|
label?: string;
|
|
name: string;
|
|
value?: string;
|
|
invalidMessage?: string;
|
|
required?: boolean;
|
|
} = $props();
|
|
|
|
const sortedTimeZones = Intl.supportedValuesOf('timeZone')
|
|
.map((timeZone) => {
|
|
// get short offset (e.g. GMT+1) for the timezone
|
|
const offset = getTimeZonePart(timeZone, 'shortOffset');
|
|
// if (!offset) return null;
|
|
|
|
// get long representation (e.g. Pacific Standard Time) for the timezone
|
|
const long = getTimeZonePart(timeZone, 'long');
|
|
|
|
// get short representation (e.g. PST) for the timezone
|
|
const short = getTimeZonePart(timeZone, 'short');
|
|
|
|
return {
|
|
timeZone,
|
|
offset,
|
|
long,
|
|
short
|
|
};
|
|
})
|
|
.filter((timeZone) => timeZone !== null)
|
|
.sort((a, b) => {
|
|
return a.timeZone.localeCompare(b.timeZone);
|
|
});
|
|
|
|
const options: ComboboxItem[] = sortedTimeZones.map((timeZone) => {
|
|
const infotext = [...new Set([timeZone.short, timeZone.offset])]
|
|
.filter((item) => item !== undefined)
|
|
.join(' · ');
|
|
|
|
return {
|
|
value: timeZone.timeZone,
|
|
label: `${timeZone.timeZone.replaceAll('_', ' ')}`,
|
|
preview: timeZone.long,
|
|
infotext: infotext,
|
|
render: timezoneLabel
|
|
};
|
|
});
|
|
const optionsMap = options.reduce(
|
|
(acc, option) => {
|
|
acc[option.value] = option;
|
|
return acc;
|
|
},
|
|
{} as Record<string, ComboboxItem>
|
|
);
|
|
|
|
let timezone = $state<ComboboxItem | undefined>(
|
|
optionsMap[Intl.DateTimeFormat().resolvedOptions().timeZone]
|
|
);
|
|
|
|
$effect(() => {
|
|
console.log(`timezone="${timezone?.value}"`);
|
|
});
|
|
</script>
|
|
|
|
<ExpandableCombobox
|
|
{label}
|
|
{name}
|
|
{invalidMessage}
|
|
{required}
|
|
bind:value={timezone}
|
|
items={options}
|
|
matchWidth
|
|
placeholder="Select a timezone"
|
|
/>
|
|
|
|
{#snippet timezoneLabel(item: ComboboxItem)}
|
|
{@html wbr(item.label ?? 'Missing label')}
|
|
{/snippet}
|