logic-mesh 0.9.0

Control logic engine using event based and reactive blocks written in Rust.
Documentation
<script lang="ts" generics="T">
	import { cn } from '$lib/utils';
	import { ChevronDown } from 'lucide-svelte';

	interface Props {
		options: T[];
		value?: T;
		optionLabel?: keyof T;
		placeholder?: string;
		class?: string;
		onchange?: (value: T) => void;
	}

	let {
		options,
		value = $bindable(undefined),
		optionLabel,
		placeholder = 'Select...',
		class: className,
		onchange,
	}: Props = $props();

	function getLabel(item: T): string {
		if (optionLabel && item && typeof item === 'object') {
			return String((item as Record<string, unknown>)[optionLabel as string] ?? '');
		}
		return String(item ?? '');
	}

	function handleChange(event: Event) {
		const idx = Number((event.target as HTMLSelectElement).value);
		const selected = options[idx];
		value = selected;
		onchange?.(selected);
	}

	const selectedIndex = $derived(value !== undefined ? options.indexOf(value) : -1);
</script>

<div class={cn('relative', className)}>
	<select
		class="border-input bg-background ring-offset-background focus:ring-ring w-full appearance-none rounded-md border py-2 pl-3 pr-8 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
		value={selectedIndex >= 0 ? String(selectedIndex) : ''}
		onchange={handleChange}
	>
		{#if selectedIndex < 0}
			<option value="" disabled>{placeholder}</option>
		{/if}
		{#each options as option, i}
			<option value={String(i)}>{getLabel(option)}</option>
		{/each}
	</select>
	<ChevronDown class="text-muted-foreground pointer-events-none absolute right-2 top-2.5 h-4 w-4" />
</div>