diff --git a/docs/superpowers/plans/2026-06-06-searchable-term-authority-picker.md b/docs/superpowers/plans/2026-06-06-searchable-term-authority-picker.md new file mode 100644 index 0000000..489ade9 --- /dev/null +++ b/docs/superpowers/plans/2026-06-06-searchable-term-authority-picker.md @@ -0,0 +1,451 @@ +# Searchable Term/Authority Picker Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the native `` used `w-full rounded border px-2 py-1 text-sm`; the popup should look like a menu surface). Concrete starting implementation (adjust class details to match the app's look; keep the structure): + +```tsx +import * as React from "react"; +import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox"; + +import { cn } from "@/lib/utils"; + +function ComboboxRoot(props: ComboboxPrimitive.Root.Props) { + return ; +} + +function ComboboxInputGroup({ className, ...props }: ComboboxPrimitive.InputGroup.Props) { + return ( + + ); +} + +function ComboboxInput({ className, ...props }: ComboboxPrimitive.Input.Props) { + return ( + + ); +} + +function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { + return ( + + ); +} + +function ComboboxTrigger({ className, ...props }: ComboboxPrimitive.Trigger.Props) { + return ( + + ); +} + +function ComboboxPopup({ className, ...props }: ComboboxPrimitive.Popup.Props) { + return ( + + + + + + ); +} + +function ComboboxList(props: ComboboxPrimitive.List.Props) { + return ; +} + +function ComboboxItem({ className, ...props }: ComboboxPrimitive.Item.Props) { + return ( + + ); +} + +function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { + return ( + + ); +} + +export { + ComboboxRoot, + ComboboxInputGroup, + ComboboxInput, + ComboboxClear, + ComboboxTrigger, + ComboboxPopup, + ComboboxList, + ComboboxItem, + ComboboxEmpty, +}; +``` +If a part's `.Props` type path differs (verify against the d.ts), adjust the type annotation — do **not** fall back to `any`. (`--anchor-width` is Base UI's positioner CSS var for matching the input width; if it isn't exposed under that name, use `min-w-[12rem]` instead — confirm when you run the story.) + +- [ ] **Step 2: Write `OptionsCombobox`** `web/src/objects/options-combobox.tsx` — the drop-in with the exact contract of the old `OptionsSelect`. It converts between the rhf `value` (id string) and the Base UI item object, and filters/displays by active-locale label. + +```tsx +import type { components } from "../api/schema"; +import { + ComboboxRoot, + ComboboxInputGroup, + ComboboxInput, + ComboboxClear, + ComboboxTrigger, + ComboboxPopup, + ComboboxList, + ComboboxItem, + ComboboxEmpty, +} from "@/components/ui/combobox"; + +type LabelView = components["schemas"]["LabelView"]; + +export type Option = { id: string; labels: LabelView[] }; + +function labelIn(labels: LabelView[], lang: string): string { + return ( + labels.find((l) => l.lang === lang)?.label ?? + labels.find((l) => l.lang === "en")?.label ?? + labels[0]?.label ?? + "" + ); +} + +export function OptionsCombobox({ + id, + value, + onChange, + options, + lang, + placeholder, +}: { + id: string; + value: string; + onChange: (v: string) => void; + options: Option[]; + lang: string; + placeholder: string; +}) { + const selected = options.find((o) => o.id === value) ?? null; + + return ( + + items={options} + value={selected} + onValueChange={(option) => onChange(option?.id ?? "")} + itemToStringLabel={(option) => (option ? labelIn(option.labels, lang) : "")} + isItemEqualToValue={(a, b) => a?.id === b?.id} + > + + + + + + + + {placeholder} + + {(option: Option) => ( + + {labelIn(option.labels, lang)} + + )} + + + + ); +} +``` +Notes: +- `labelIn` is duplicated here from `field-input.tsx`. In Task 2 you will **export `labelIn` from a shared spot** (see Task 2 Step 3) and import it in both — for now define it locally so this file compiles standalone; Task 2 dedupes. +- Confirm the generic on `ComboboxRoot