authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-17 20:17:51-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-17 20:17:51-07:00
log8583eac643b8c248bcd835e8de81c7c0d7182ad3
tree4e0b4fa55a2483f03f4956cfe11c4b58251a42a3
parent53510d44ea7b4a44c10c122dd9245194f2f14d09
signature Signed by PGP key B5690EEEBB952194

refactor(web): reuse searchable picker inputs (#12353)


5 files changed, 28 insertions(+), 82 deletions(-)

apps/web/src/components/BranchToolbarBranchSelector.tsx+7-20
...@@ -9,7 +9,7 @@ import {...@@ -9,7 +9,7 @@ import {
9} from "@t3tools/client-runtime/state/runtime";9} from "@t3tools/client-runtime/state/runtime";
10import type { ContextMenuItem, EnvironmentId, VcsRef, ThreadId } from "@t3tools/contracts";10import type { ContextMenuItem, EnvironmentId, VcsRef, ThreadId } from "@t3tools/contracts";
11import { LegendList, type LegendListRef } from "@legendapp/list/react";11import { LegendList, type LegendListRef } from "@legendapp/list/react";
12import { ChevronDownIcon, GitBranchIcon, SearchIcon } from "lucide-react";12import { ChevronDownIcon, GitBranchIcon } from "lucide-react";
13import {13import {
14 useCallback,14 useCallback,
15 useDeferredValue,15 useDeferredValue,
...@@ -64,7 +64,7 @@ import { getVirtualizedScrollFadeClassName } from "./ui/scroll-area";...@@ -64,7 +64,7 @@ import { getVirtualizedScrollFadeClassName } from "./ui/scroll-area";
64import {64import {
65 Combobox,65 Combobox,
66 ComboboxEmpty,66 ComboboxEmpty,
67 ComboboxInput,67 ComboboxSearchInput,
68 ComboboxItem,68 ComboboxItem,
69 ComboboxListVirtualized,69 ComboboxListVirtualized,
70 ComboboxPopup,70 ComboboxPopup,
...@@ -824,24 +824,11 @@ export function BranchToolbarBranchSelector({...@@ -824,24 +824,11 @@ export function BranchToolbarBranchSelector({
824 className="flex w-80 flex-col"824 className="flex w-80 flex-col"
825 {...composerFloatingLayerProps}825 {...composerFloatingLayerProps}
826 >826 >
827 <div className="shrink-0 px-3 pt-2.5">827 <ComboboxSearchInput
828 <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">828 placeholder="Search refs..."
829 <SearchIcon829 value={branchQuery}
830 aria-hidden="true"830 onChange={(event) => setBranchQuery(event.target.value)}
831 className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55"831 />
832 />
833 <ComboboxInput
834 className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5"
835 inputClassName="rounded-none bg-transparent text-sm"
836 placeholder="Search refs..."
837 showTrigger={false}
838 size="sm"
839 unstyled
840 value={branchQuery}
841 onChange={(event) => setBranchQuery(event.target.value)}
842 />
843 </div>
844 </div>
845 <div className="flex min-h-0 flex-1 flex-col overflow-hidden">832 <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
846 <ComboboxEmpty>No refs found.</ComboboxEmpty>833 <ComboboxEmpty>No refs found.</ComboboxEmpty>
847 <div className="relative min-h-0 w-full max-h-56 flex-1 overflow-hidden">834 <div className="relative min-h-0 w-full max-h-56 flex-1 overflow-hidden">
apps/web/src/components/DiffPanel.tsx+6-20
...@@ -19,7 +19,6 @@ import {...@@ -19,7 +19,6 @@ import {
19 FolderTreeIcon,19 FolderTreeIcon,
20 PilcrowIcon,20 PilcrowIcon,
21 Rows3Icon,21 Rows3Icon,
22 SearchIcon,
23 TextWrapIcon,22 TextWrapIcon,
24} from "lucide-react";23} from "lucide-react";
25import * as Schema from "effect/Schema";24import * as Schema from "effect/Schema";
...@@ -64,7 +63,7 @@ import { Switch } from "./ui/switch";...@@ -64,7 +63,7 @@ import { Switch } from "./ui/switch";
64import {63import {
65 Combobox,64 Combobox,
66 ComboboxEmpty,65 ComboboxEmpty,
67 ComboboxInput,66 ComboboxSearchInput,
68 ComboboxItem,67 ComboboxItem,
69 ComboboxList,68 ComboboxList,
70 ComboboxPopup,69 ComboboxPopup,
...@@ -757,24 +756,11 @@ export default function DiffPanel({...@@ -757,24 +756,11 @@ export default function DiffPanel({
757 align="start"756 align="start"
758 className="w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden"757 className="w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden"
759 >758 >
760 <div className="min-w-0 shrink-0 px-3 pt-2.5">759 <ComboboxSearchInput
761 <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">760 placeholder="Search refs..."
762 <SearchIcon761 value={baseRefQuery}
763 aria-hidden="true"762 onChange={(event) => setBaseRefQuery(event.target.value)}
764 className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55"763 />
765 />
766 <ComboboxInput
767 className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5"
768 inputClassName="rounded-none bg-transparent text-sm"
769 placeholder="Search refs..."
770 showTrigger={false}
771 size="sm"
772 unstyled
773 value={baseRefQuery}
774 onChange={(event) => setBaseRefQuery(event.target.value)}
775 />
776 </div>
777 </div>
778 <div className="grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide">764 <div className="grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide">
779 <span aria-hidden="true" />765 <span aria-hidden="true" />
780 <div className="grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center">766 <div className="grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center">
apps/web/src/components/pullRequest/PullRequestCandidatePicker.tsx+7-21
...@@ -6,13 +6,12 @@...@@ -6,13 +6,12 @@
6 * The same combobox as the project and branch pickers, and dressed the same, rather than a menu:6 * The same combobox as the project and branch pickers, and dressed the same, rather than a menu:
7 * a menu's typeahead claims every keypress to jump between rows, which a search box cannot share.7 * a menu's typeahead claims every keypress to jump between rows, which a search box cannot share.
8 */8 */
9import { SearchIcon } from "lucide-react";
10import type { ReactNode } from "react";9import type { ReactNode } from "react";
1110
12import { Button } from "../ui/button";11import { Button } from "../ui/button";
13import {12import {
14 Combobox,13 Combobox,
15 ComboboxInput,14 ComboboxSearchInput,
16 ComboboxItem,15 ComboboxItem,
17 ComboboxList,16 ComboboxList,
18 ComboboxPopup,17 ComboboxPopup,
...@@ -123,25 +122,12 @@ export function PullRequestCandidatePicker<T>({...@@ -123,25 +122,12 @@ export function PullRequestCandidatePicker<T>({
123 }122 }
124 />123 />
125 <ComboboxPopup align="start" side="bottom" className="w-72">124 <ComboboxPopup align="start" side="bottom" className="w-72">
126 <div className="shrink-0 px-3 pt-2.5">125 <ComboboxSearchInput
127 <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">126 aria-label={searchLabel}
128 <SearchIcon127 placeholder={searchLabel}
129 aria-hidden="true"128 value={query}
130 className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55"129 onChange={(event) => onQueryChange(event.target.value)}
131 />130 />
132 <ComboboxInput
133 aria-label={searchLabel}
134 className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5"
135 inputClassName="rounded-none bg-transparent text-sm"
136 placeholder={searchLabel}
137 showTrigger={false}
138 size="sm"
139 unstyled
140 value={query}
141 onChange={(event) => onQueryChange(event.target.value)}
142 />
143 </div>
144 </div>
145 <ComboboxList className="max-h-72">131 <ComboboxList className="max-h-72">
146 {isPending ? (132 {isPending ? (
147 <PullRequestPeopleGhost rows={4} />133 <PullRequestPeopleGhost rows={4} />
apps/web/src/components/settings/FontFamilyPicker.tsx+7-20
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1import { LegendList, type LegendListRef } from "@legendapp/list/react";1import { LegendList, type LegendListRef } from "@legendapp/list/react";
2import { CheckIcon, ChevronDownIcon, SearchIcon } from "lucide-react";2import { CheckIcon, ChevronDownIcon } from "lucide-react";
3import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";3import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4import { isMonospaceFamily, queryInstalledFontFamilies } from "../../appearanceFonts";4import { isMonospaceFamily, queryInstalledFontFamilies } from "../../appearanceFonts";
5import {5import {
6 Combobox,6 Combobox,
7 ComboboxEmpty,7 ComboboxEmpty,
8 ComboboxInput,8 ComboboxSearchInput,
9 ComboboxItem,9 ComboboxItem,
10 ComboboxListVirtualized,10 ComboboxListVirtualized,
11 ComboboxPopup,11 ComboboxPopup,
...@@ -211,24 +211,11 @@ export function FontFamilyPicker({...@@ -211,24 +211,11 @@ export function FontFamilyPicker({
211 <ChevronDownIcon className="-me-1 size-3 opacity-50" />211 <ChevronDownIcon className="-me-1 size-3 opacity-50" />
212 </ComboboxTrigger>212 </ComboboxTrigger>
213 <ComboboxPopup align="end" className="flex w-72 flex-col">213 <ComboboxPopup align="end" className="flex w-72 flex-col">
214 <div className="shrink-0 px-3 pt-2.5">214 <ComboboxSearchInput
215 <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">215 placeholder="Search fonts…"
216 <SearchIcon216 value={query}
217 aria-hidden="true"217 onChange={(event) => setQuery(event.target.value)}
218 className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55"218 />
219 />
220 <ComboboxInput
221 className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5"
222 inputClassName="rounded-none bg-transparent text-sm"
223 placeholder="Search fonts…"
224 showTrigger={false}
225 size="sm"
226 unstyled
227 value={query}
228 onChange={(event) => setQuery(event.target.value)}
229 />
230 </div>
231 </div>
232 <div className="flex min-h-0 flex-1 flex-col overflow-hidden">219 <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
233 <ComboboxEmpty>No fonts found.</ComboboxEmpty>220 <ComboboxEmpty>No fonts found.</ComboboxEmpty>
234 <div className="relative min-h-0 max-h-72 w-full flex-1 overflow-hidden">221 <div className="relative min-h-0 max-h-72 w-full flex-1 overflow-hidden">
apps/web/src/components/ui/combobox.tsx+1-1
...@@ -132,7 +132,7 @@ function ComboboxInput({...@@ -132,7 +132,7 @@ function ComboboxInput({
132132
133function ComboboxSearchInput(props: React.ComponentProps<typeof ComboboxInput>) {133function ComboboxSearchInput(props: React.ComponentProps<typeof ComboboxInput>) {
134 return (134 return (
135 <div className="shrink-0 px-3 pt-2.5">135 <div className="min-w-0 shrink-0 px-3 pt-2.5">
136 <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">136 <div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">
137 <SearchIcon137 <SearchIcon
138 aria-hidden="true"138 aria-hidden="true"