fix: replace bits-ui Select with native HTML select
- Remove bits-ui Select component dependency - Use native HTML select element for device selection - Simplify state management (single mappings Map) - Fix selection handling with direct onchange event - Add visual indicator for exact name matches - Resolves selection not working issue
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import * as Dialog from "$lib/components/ui/dialog";
|
import * as Dialog from "$lib/components/ui/dialog";
|
||||||
import * as Select from "$lib/components/ui/select";
|
|
||||||
import Button from "$lib/components/ui/button/button.svelte";
|
import Button from "$lib/components/ui/button/button.svelte";
|
||||||
import type { BluetoothDevice, DeviceInfo } from "$lib/types";
|
import type { BluetoothDevice, DeviceInfo } from "$lib/types";
|
||||||
|
|
||||||
@@ -18,9 +17,6 @@ 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(
|
||||||
@@ -42,7 +38,6 @@ 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
|
||||||
@@ -58,12 +53,10 @@ $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;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,18 +69,14 @@ function handleConfirm() {
|
|||||||
onConfirm(mappings);
|
onConfirm(mappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeviceSelect(recordedDeviceName: string, selected: { value: string; label: string } | undefined) {
|
function handleDeviceSelect(recordedDeviceName: string, deviceId: string) {
|
||||||
if (!selected?.value) return;
|
if (!deviceId) return;
|
||||||
|
|
||||||
const device = connectedDevices.find(d => d.id === selected.value);
|
const device = connectedDevices.find(d => d.id === deviceId);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +97,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 currentSelected = selectedValues.get(recordedDevice.name)}
|
{@const currentMapping = mappings.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">
|
||||||
@@ -134,29 +123,19 @@ const allDevicesMapped = $derived(
|
|||||||
<span class="text-sm">No compatible devices</span>
|
<span class="text-sm">No compatible devices</span>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<Select.Root
|
<select
|
||||||
selected={currentSelected}
|
class="w-full px-3 py-2 rounded-md border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||||
onSelectedChange={(selected) => {
|
value={currentMapping?.id || ''}
|
||||||
handleDeviceSelect(recordedDevice.name, selected);
|
onchange={(e) => handleDeviceSelect(recordedDevice.name, e.currentTarget.value)}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Select.Trigger class="w-full">
|
<option value="" disabled>Select device...</option>
|
||||||
<Select.Value placeholder="Select device..." />
|
{#each compatibleDevices as device}
|
||||||
</Select.Trigger>
|
<option value={device.id}>
|
||||||
<Select.Content>
|
{device.name}
|
||||||
{#each compatibleDevices as device}
|
{#if device.name === recordedDevice.name}(exact match){/if}
|
||||||
<Select.Item value={device.id} label={device.name}>
|
</option>
|
||||||
<div class="flex items-center gap-2">
|
{/each}
|
||||||
<span class="icon-[ri--bluetooth-line] w-4 h-4"></span>
|
</select>
|
||||||
<span>{device.name}</span>
|
|
||||||
{#if device.name === recordedDevice.name}
|
|
||||||
<span class="icon-[ri--checkbox-circle-fill] w-4 h-4 text-green-500"></span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</Select.Item>
|
|
||||||
{/each}
|
|
||||||
</Select.Content>
|
|
||||||
</Select.Root>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user