Skip to content

Commit 1551338

Browse files
vibeguiclaude
andcommitted
feat(shell): org switcher opens focused on search, filters as you type
The switcher popover no longer hides search behind a toggle: the field is always visible with a leading icon, and the popover lands focus on it via onOpenAutoFocus — so typing filters orgs immediately on open. Esc clears the query. Drops the search-toggle state and the now-unused XClose icon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f0a3d54 commit 1551338

1 file changed

Lines changed: 25 additions & 31 deletions

File tree

apps/mesh/src/web/components/header/org-switcher.tsx

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
PopoverContent,
1414
PopoverTrigger,
1515
} from "@deco/ui/components/popover.tsx";
16-
import { Check, Plus, SearchMd, XClose } from "@untitledui/icons";
16+
import { Check, Plus, SearchMd } from "@untitledui/icons";
1717
import { cn } from "@deco/ui/lib/utils.ts";
1818
import { useActiveOrganizations } from "@/web/lib/auth-client";
1919
import { CreateOrganizationDialog } from "@/web/components/create-organization-dialog";
@@ -82,7 +82,6 @@ function OrganizationsPanel({
8282
return a.name.localeCompare(b.name);
8383
});
8484

85-
const [searchOpen, setSearchOpen] = useState(false);
8685
const [query, setQuery] = useState("");
8786

8887
const q = query.toLowerCase();
@@ -96,37 +95,24 @@ function OrganizationsPanel({
9695
const iconBtnClass =
9796
"flex items-center justify-center size-7 rounded-md text-muted-foreground hover:bg-accent/50 hover:text-foreground transition-colors";
9897

99-
function toggleSearch() {
100-
if (searchOpen) setQuery("");
101-
setSearchOpen((prev) => !prev);
102-
}
103-
10498
return (
10599
<>
106-
<div className="flex items-center justify-between px-4 py-3">
107-
{searchOpen ? (
108-
<input
109-
autoFocus
110-
type="text"
111-
value={query}
112-
onChange={(e) => setQuery(e.target.value)}
113-
onKeyDown={(e) => e.key === "Escape" && toggleSearch()}
114-
placeholder="Search organizations..."
115-
className="flex-1 min-w-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground/60"
116-
/>
117-
) : (
118-
<span className="text-sm font-medium text-muted-foreground/60">
119-
Your Organizations
120-
</span>
121-
)}
122-
<div className="flex items-center gap-1 shrink-0">
123-
<button type="button" onClick={toggleSearch} className={iconBtnClass}>
124-
{searchOpen ? <XClose size={16} /> : <SearchMd size={16} />}
125-
</button>
126-
<button type="button" onClick={onCreateOrg} className={iconBtnClass}>
127-
<Plus size={16} />
128-
</button>
129-
</div>
100+
{/* Search is always live and focused on open — start typing to filter. */}
101+
<div className="flex items-center gap-2 px-3 py-2.5 border-b border-border/50">
102+
<SearchMd size={16} className="shrink-0 text-muted-foreground/60" />
103+
<input
104+
data-org-switcher-search
105+
autoFocus
106+
type="text"
107+
value={query}
108+
onChange={(e) => setQuery(e.target.value)}
109+
onKeyDown={(e) => e.key === "Escape" && query && setQuery("")}
110+
placeholder="Search organizations..."
111+
className="flex-1 min-w-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground/60"
112+
/>
113+
<button type="button" onClick={onCreateOrg} className={iconBtnClass}>
114+
<Plus size={16} />
115+
</button>
130116
</div>
131117
<div className="flex-1 min-h-0 overflow-y-auto p-1.5 flex flex-col gap-1">
132118
{filtered.length === 0 && (
@@ -198,6 +184,14 @@ export function OrgSwitcherPopover({
198184
sideOffset={8}
199185
collisionPadding={16}
200186
className="w-[300px] p-0 flex flex-col max-h-[440px]"
187+
// Land focus on the search field (not the content wrapper) so typing
188+
// filters immediately.
189+
onOpenAutoFocus={(e) => {
190+
e.preventDefault();
191+
(e.currentTarget as HTMLElement)
192+
?.querySelector<HTMLInputElement>("[data-org-switcher-search]")
193+
?.focus();
194+
}}
201195
onCloseAutoFocus={(e) => e.preventDefault()}
202196
>
203197
<OrganizationsPanel

0 commit comments

Comments
 (0)