// ~/src/siteplane/components/autocomplete/autocomplete.tsx "use client"; import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete"; import { ChevronsUpDownIcon, XIcon } from "lucide-react"; import * as React from "react"; import { useFormReset } from "../../system/form/use-form-reset"; import { type SiteplaneControlSize, SiteplaneUIPortalScope, useResolvedControlSize, } from "../../system/provider/siteplane-ui-provider"; import { type FormControlSurface, surfaceClassNames, } from "../../system/styles/surface"; import { cn, type WithStringClassName } from "../../system/utils/cn"; import { baseClass, slotClass } from "../../system/utils/slot-class"; import { Button } from "../button"; import { Separator } from "../separator"; import { Input } from "../input"; import { KitInput } from "../input/input-internal"; import { ExternalInputOwner } from "../input/input"; import { ScrollArea } from "../scroll-area"; import type { KitAutocompleteInputProps, KitAutocompleteItemProps, } from "./autocomplete-internal"; export const Autocomplete: typeof AutocompletePrimitive.Root = ( props: Omit, "items"> & { items?: readonly ItemValue[] | undefined; }, ) => { const [resetKey, setResetKey] = React.useState(0); const markerRef = useFormReset(props.form, () => { if (props.value === undefined) setResetKey((current) => current + 1); }); return ( <> ); }; type AutocompleteInputProps = Omit< WithStringClassName, "size" > & { showTrigger?: boolean; showClear?: boolean; startAddon?: React.ReactNode; size?: SiteplaneControlSize | number | undefined; variant?: FormControlSurface | undefined; unstyled?: boolean; ref?: React.Ref; triggerProps?: WithStringClassName; clearProps?: WithStringClassName; }; export function AutocompleteInput( inputProps: AutocompleteInputProps, ): React.ReactElement { // Command passes the internal command profile through // KitAutocompleteInput (autocomplete-internal.ts), the same function. const { className, disabled, showTrigger = false, showClear = false, startAddon, size, unstyled = false, profile = "combobox", variant = "default", triggerProps, clearProps, ...props } = inputProps as KitAutocompleteInputProps; const visualSize = typeof size === "number" ? undefined : size; const nativeSize = typeof size === "number" ? size : undefined; const resolvedSize = useResolvedControlSize(visualSize); return ( *.w-full]:w-fit", )} data-slot="autocomplete-input-group" > {startAddon && ( )} {/* Autocomplete.Input already connects the input to Field. */} } disabled={disabled} {...props} /> {showTrigger && ( ) } > )} {showClear && ( ) } > )} ); } export function AutocompletePopup({ className, children, side = "bottom", sideOffset = 4, alignOffset, align = "start", anchor, portalProps, ...props }: WithStringClassName & { align?: AutocompletePrimitive.Positioner.Props["align"]; sideOffset?: AutocompletePrimitive.Positioner.Props["sideOffset"]; alignOffset?: AutocompletePrimitive.Positioner.Props["alignOffset"]; side?: AutocompletePrimitive.Positioner.Props["side"]; anchor?: AutocompletePrimitive.Positioner.Props["anchor"]; portalProps?: WithStringClassName; }): React.ReactElement { return ( {children} ); } export function AutocompleteItem( itemProps: WithStringClassName, ): React.ReactElement { // Command passes the internal command profile through KitAutocompleteItem // (autocomplete-internal.ts), the same function. const { profile = "default", className, children, ...props } = itemProps as KitAutocompleteItemProps; return ( svg:first-child]:grid has-[>svg:first-child]:auto-cols-[auto_minmax(0,1fr)] has-[>svg:first-child]:grid-flow-col has-[>svg:first-child]:content-center has-[>svg:first-child]:items-start [&>svg:first-child]:min-h-lh", className, )} data-foundation="autocomplete-item" data-profile={profile} data-slot="autocomplete-item" {...props} > {children} ); } export function AutocompleteSeparator({ className, ...props }: WithStringClassName): React.ReactElement { return ( } {...props} /> ); } export function AutocompleteGroup({ className, ...props }: WithStringClassName): React.ReactElement { return ( ); } export function AutocompleteGroupLabel({ className, ...props }: WithStringClassName): React.ReactElement { return ( ); } export function AutocompleteEmpty({ className, ...props }: WithStringClassName): React.ReactElement { return ( ); } export function AutocompleteRow({ className, ...props }: WithStringClassName): React.ReactElement { return ( ); } export function AutocompleteValue({ ...props }: AutocompletePrimitive.Value.Props): React.ReactElement { return ( ); } export function AutocompleteList({ className, ...props }: WithStringClassName): React.ReactElement { return ( ); } export function AutocompleteClear({ className, render, ...props }: WithStringClassName): React.ReactElement { return ( ) } {...props} > ); } export function AutocompleteStatus({ className, ...props }: WithStringClassName): React.ReactElement { return ( ); } export function AutocompleteCollection({ ...props }: AutocompletePrimitive.Collection.Props): React.ReactElement { return ( ); } export function AutocompleteTrigger({ className, children, render, ...props }: WithStringClassName): React.ReactElement { return ( ) } {...props} > {children} ); } export const useAutocompleteFilter: typeof AutocompletePrimitive.useFilter = AutocompletePrimitive.useFilter; export { AutocompletePrimitive }; // ~/src/siteplane/components/autocomplete/autocomplete-internal.ts import type * as React from "react"; import { AutocompleteInput, AutocompleteItem } from "./autocomplete"; /* * Kit-internal Autocomplete parts. Not public API: the autocomplete index does * not export this module, and its names and profiles may change in any * release. Command renders its search input and items with the command * profile. */ export type KitAutocompleteInputProps = React.ComponentProps< typeof AutocompleteInput > & { profile?: "combobox" | "command" }; export type KitAutocompleteItemProps = React.ComponentProps< typeof AutocompleteItem > & { profile?: "default" | "command" }; /** AutocompleteInput with the kit's composition profiles; the same component. */ export const KitAutocompleteInput = AutocompleteInput as ( props: KitAutocompleteInputProps, ) => React.ReactElement; /** AutocompleteItem with the kit's composition profile; the same component. */ export const KitAutocompleteItem = AutocompleteItem as ( props: KitAutocompleteItemProps, ) => React.ReactElement; // ~/src/siteplane/components/autocomplete/index.ts export * from "./autocomplete";