fix: proper state binding for Select component in device mapping

- Add separate selectedValues state map for Select component binding
- Update handleDeviceSelect to manage both mappings and selectedValues
- Bind currentSelected directly to Select.Root selected prop
- Pass full selected object in onSelectedChange callback
- Ensures Select component properly reflects user selections in Svelte 5
This commit is contained in:
Valknar XXX
2025-10-28 05:48:25 +01:00
parent 6e94ec99bc
commit a959186de7

View File

@@ -18,6 +18,9 @@ let { open, recordedDevices, connectedDevices, onConfirm, onCancel }: Props = $p
// Device mappings: recorded device name -> connected device // Device mappings: recorded device name -> connected device
let mappings = $state<Map<string, BluetoothDevice>>(new Map()); let mappings = $state<Map<string, BluetoothDevice>>(new Map());
// Selected values for each device (for Select component binding)
let selectedValues = $state<Map<string, { value: string; label: string }>>(new Map());
// Check if a connected device is compatible with a recorded device // Check if a connected device is compatible with a recorded device
function isCompatible(recordedDevice: DeviceInfo, connectedDevice: BluetoothDevice): boolean { function isCompatible(recordedDevice: DeviceInfo, connectedDevice: BluetoothDevice): boolean {
const connectedActuators = connectedDevice.info.messageAttributes.ScalarCmd.map( const connectedActuators = connectedDevice.info.messageAttributes.ScalarCmd.map(
@@ -39,6 +42,7 @@ function getCompatibleDevices(recordedDevice: DeviceInfo): BluetoothDevice[] {
$effect(() => { $effect(() => {
if (open && recordedDevices.length > 0 && connectedDevices.length > 0) { if (open && recordedDevices.length > 0 && connectedDevices.length > 0) {
const newMappings = new Map<string, BluetoothDevice>(); const newMappings = new Map<string, BluetoothDevice>();
const newSelectedValues = new Map<string, { value: string; label: string }>();
recordedDevices.forEach(recordedDevice => { recordedDevices.forEach(recordedDevice => {
// Try to find exact name match first // Try to find exact name match first
@@ -54,10 +58,12 @@ $effect(() => {
if (match) { if (match) {
newMappings.set(recordedDevice.name, match); newMappings.set(recordedDevice.name, match);
newSelectedValues.set(recordedDevice.name, { value: match.id, label: match.name });
} }
}); });
mappings = newMappings; mappings = newMappings;
selectedValues = newSelectedValues;
} }
}); });
@@ -70,12 +76,18 @@ function handleConfirm() {
onConfirm(mappings); onConfirm(mappings);
} }
function handleDeviceSelect(recordedDeviceName: string, selectedDeviceId: string) { function handleDeviceSelect(recordedDeviceName: string, selected: { value: string; label: string } | undefined) {
const device = connectedDevices.find(d => d.id === selectedDeviceId); if (!selected?.value) return;
const device = connectedDevices.find(d => d.id === selected.value);
if (device) { if (device) {
const newMappings = new Map(mappings); const newMappings = new Map(mappings);
newMappings.set(recordedDeviceName, device); newMappings.set(recordedDeviceName, device);
mappings = newMappings; mappings = newMappings;
const newSelectedValues = new Map(selectedValues);
newSelectedValues.set(recordedDeviceName, selected);
selectedValues = newSelectedValues;
} }
} }
@@ -96,7 +108,7 @@ const allDevicesMapped = $derived(
<div class="space-y-4 py-4"> <div class="space-y-4 py-4">
{#each recordedDevices as recordedDevice} {#each recordedDevices as recordedDevice}
{@const compatibleDevices = getCompatibleDevices(recordedDevice)} {@const compatibleDevices = getCompatibleDevices(recordedDevice)}
{@const currentMapping = mappings.get(recordedDevice.name)} {@const currentSelected = selectedValues.get(recordedDevice.name)}
<div class="flex items-center gap-4 p-4 bg-muted/30 rounded-lg border border-border/50"> <div class="flex items-center gap-4 p-4 bg-muted/30 rounded-lg border border-border/50">
<div class="flex-1"> <div class="flex-1">
@@ -123,11 +135,9 @@ const allDevicesMapped = $derived(
</div> </div>
{:else} {:else}
<Select.Root <Select.Root
selected={{ value: currentMapping?.id || '', label: currentMapping?.name || 'Select device...' }} selected={currentSelected}
onSelectedChange={(selected) => { onSelectedChange={(selected) => {
if (selected?.value) { handleDeviceSelect(recordedDevice.name, selected);
handleDeviceSelect(recordedDevice.name, selected.value);
}
}} }}
> >
<Select.Trigger class="w-full"> <Select.Trigger class="w-full">