File size: 3,225 Bytes
336b102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74473f3
336b102
 
 
 
 
 
 
 
c6afb12
336b102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c6afb12
336b102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<script lang="ts" module>
	import type { Snippet } from 'svelte';

	export type SelectFieldOption = {
		value: string;
		label: string;
	};

	export type SelectFieldProps = {
		id: string;
		label: string;
		options: SelectFieldOption[];
		value?: string | string[];
		type?: 'single' | 'multiple';
		triggerLabel: string;
		containerClass?: string;
		triggerClass?: string;
		contentClass?: string;
		showLabel?: boolean;
		separatorAfterValue?: string;
		onValueChange?: ((value: string) => void) | ((value: string[]) => void);
		item?: Snippet<[SelectFieldOption]>;
	};
</script>

<script lang="ts">
	import * as Select from '$lib/components/ui/select';

	let {
		id,
		label,
		options,
		value = $bindable(),
		type = 'single',
		triggerLabel,
		containerClass = 'min-w-0 space-y-2',
		triggerClass = 'w-full',
		contentClass,
		showLabel = true,
		separatorAfterValue,
		onValueChange,
		item
	}: SelectFieldProps = $props();

	function getSingleValue() {
		return typeof value === 'string' ? value : undefined;
	}

	function setSingleValue(nextValue: string | undefined) {
		if (nextValue === undefined) return;
		value = nextValue;
		(onValueChange as ((value: string) => void) | undefined)?.(nextValue);
	}

	function getMultipleValue() {
		return Array.isArray(value) ? value : undefined;
	}

	function setMultipleValue(nextValue: string[] | undefined) {
		if (nextValue === undefined) return;
		value = nextValue;
		(onValueChange as ((value: string[]) => void) | undefined)?.(nextValue);
	}
</script>

<div class={containerClass}>
	{#if showLabel}
		<span {id} class="block text-xs font-semibold tracking-wider text-muted-foreground uppercase">
			{label}
		</span>
	{/if}
	{#if type === 'multiple'}
		<Select.Root type="multiple" bind:value={getMultipleValue, setMultipleValue} items={options}>
			<Select.Trigger aria-labelledby={id} class={triggerClass}>
				<span data-slot="select-value">{triggerLabel}</span>
			</Select.Trigger>
			<Select.Content preventScroll={false} class={contentClass}>
				<Select.Group>
					{#each options as option (option.value)}
						{#if item}
							<Select.Item value={option.value} label={option.label}>
								{@render item(option)}
							</Select.Item>
						{:else}
							<Select.Item value={option.value} label={option.label} />
						{/if}
						{#if option.value === separatorAfterValue}
							<Select.Separator />
						{/if}
					{/each}
				</Select.Group>
			</Select.Content>
		</Select.Root>
	{:else}
		<Select.Root type="single" bind:value={getSingleValue, setSingleValue} items={options}>
			<Select.Trigger aria-labelledby={id} class={triggerClass}>
				<span data-slot="select-value">{triggerLabel}</span>
			</Select.Trigger>
			<Select.Content preventScroll={false} class={contentClass}>
				<Select.Group>
					{#each options as option (option.value)}
						{#if item}
							<Select.Item value={option.value} label={option.label}>
								{@render item(option)}
							</Select.Item>
						{:else}
							<Select.Item value={option.value} label={option.label} />
						{/if}
						{#if option.value === separatorAfterValue}
							<Select.Separator />
						{/if}
					{/each}
				</Select.Group>
			</Select.Content>
		</Select.Root>
	{/if}
</div>