Skip to content

Commit e65ae98

Browse files
itsmeakhilclaude
andcommitted
feat(data-explorer): make the data source picker a dropdown
Replaces the two-step grid-of-buttons picker with an always-visible Select at the top of the connection dialog. Creating defaults to the first registered source so the dialog opens ready to type; editing pins the source, since changing what kind of database an existing connection points at was never coherent. Switching source clears any error and test result from the previous one, and the adapter form is keyed by adapter id so no field carries over. Reuses the existing connectionDialog.pickSource label, so no new keys and no change to the 27-locale parity. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a8a4abe commit e65ae98

1 file changed

Lines changed: 49 additions & 20 deletions

File tree

apps/desktop-ui/src/components/data-explorer/connection-dialog.tsx

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ import {
99
DialogHeader,
1010
DialogTitle,
1111
} from "@/components/ui/dialog";
12-
import { Button } from "@/components/ui/button";
12+
import { Label } from "@/components/ui/label";
13+
import {
14+
Select,
15+
SelectContent,
16+
SelectItem,
17+
SelectTrigger,
18+
SelectValue,
19+
} from "@/components/ui/select";
1320
import { cn } from "@/lib/utils";
1421
import { useMasterKeyStore } from "@/store/master-key-store";
1522
import { SOURCES, SOURCE_ORDER, getAdapter } from "./sources";
@@ -34,14 +41,24 @@ export function ConnectionDialog({ open, onOpenChange, editing, onSaved }: Conne
3441

3542
useEffect(() => {
3643
if (!open) return;
37-
setSourceId(editing?.sourceId ?? null);
44+
// Creating defaults to the first registered source so the dialog opens
45+
// ready to type. Editing pins the connection's own source — changing
46+
// what kind of database an existing connection is was never coherent.
47+
setSourceId(editing?.sourceId ?? SOURCE_ORDER[0] ?? null);
3848
setError(null);
3949
setSaving(false);
4050
setTestState("idle");
4151
}, [open, editing]);
4252

4353
const adapter = sourceId ? getAdapter(sourceId) : null;
4454

55+
/** Switching source discards any error or test result from the previous one. */
56+
function handleSourceChange(next: SourceId) {
57+
setSourceId(next);
58+
setError(null);
59+
setTestState("idle");
60+
}
61+
4562
/**
4663
* Explicit, optional user action. Saving never waits on it — a rename or a
4764
* recolour must still persist off-VPN, and a connection to a server that is
@@ -108,31 +125,43 @@ export function ConnectionDialog({ open, onOpenChange, editing, onSaved }: Conne
108125
</DialogTitle>
109126
</DialogHeader>
110127

111-
{!adapter ? (
112-
<div className="space-y-2">
113-
<p className="text-sm text-muted-foreground">
114-
{t("connectionDialog.pickSource")}
115-
</p>
116-
<div className="grid grid-cols-2 gap-2">
128+
<div className="space-y-1.5">
129+
<Label htmlFor="data-explorer-source">
130+
{t("connectionDialog.pickSource")}
131+
</Label>
132+
<Select
133+
value={sourceId ?? undefined}
134+
onValueChange={handleSourceChange}
135+
// Editing pins the source: an existing connection cannot
136+
// change what kind of database it points at.
137+
disabled={!!editing || saving}
138+
>
139+
<SelectTrigger id="data-explorer-source" className="w-full">
140+
<SelectValue />
141+
</SelectTrigger>
142+
<SelectContent>
117143
{SOURCE_ORDER.map((id) => {
118144
const candidate = SOURCES[id];
119145
const Icon = candidate.icon;
120146
return (
121-
<Button
122-
key={id}
123-
variant="outline"
124-
className="h-auto justify-start gap-2 py-3"
125-
onClick={() => setSourceId(id)}
126-
>
127-
<Icon className={cn("size-5", candidate.accent)} />
128-
<span>{candidate.label}</span>
129-
</Button>
147+
<SelectItem key={id} value={id}>
148+
<span className="flex items-center gap-2">
149+
<Icon className={cn("size-4", candidate.accent)} />
150+
{/* Proper noun — never translated. */}
151+
{candidate.label}
152+
</span>
153+
</SelectItem>
130154
);
131155
})}
132-
</div>
133-
</div>
134-
) : (
156+
</SelectContent>
157+
</Select>
158+
</div>
159+
160+
{adapter && (
135161
<adapter.ConnectionForm
162+
// Remount on source change so no field carries over
163+
// from the previous adapter's form.
164+
key={adapter.id}
136165
initial={{
137166
name: editing?.name ?? "",
138167
folder: editing?.folder ?? "",

0 commit comments

Comments
 (0)