import React, { type JSX, forwardRef, useState } from "react"; import { Search } from "../../form/search"; import { VStack } from "../../primitives/stack"; import { BodyShort, Detail } from "../../typography"; import Listbox from "../../utils/components/Listbox/root/ListboxRoot"; import { DismissableLayer } from "../../utils/components/dismissablelayer/DismissableLayer"; import { Floating } from "../../utils/components/floating/Floating"; import { useMergeRefsN } from "../../utils/hooks"; import type { AutoCompleteOption, OptionGroup } from "./AutoSuggest.types"; interface AutoSuggestProps { options: OptionGroup[]; onSelect: (option: AutoCompleteOption) => boolean; className?: string; value: string; onChange: (newValue: string) => void; open: boolean; setOpen: (open: boolean) => void; } const AutoSuggest = forwardRef( ({ options, onSelect, value, onChange, open, setOpen }, ref) => { const [virtuallyFocusedOptionId, setVirtuallyFocusedOptionId] = useState(""); const [inputRef, setInputRef] = useState(null); /* Unsure why N version works, but not regular here */ const mergedRef = useMergeRefsN([setInputRef, ref]); const handleClose = () => { setOpen(false); }; const handleChange = (newValue: string) => { onChange(newValue); setOpen(true); }; const handleSelectOption = (option: AutoCompleteOption) => { const createdNewToken = onSelect(option); if (createdNewToken) { inputRef?.focus(); setOpen(false); } }; return ( { setOpen(true); }} onFocus={() => setOpen(true)} size="small" autoComplete="off" /* onKeyDown={(e) => { if (e.key === "Enter") { createToken(filterText); } }} */ /> {open && ( )} ); }, ); type AutoSuggestPopupProps = { options: OptionGroup[]; onSelect: (option: AutoCompleteOption) => void; focusedValue: string; setFocusedValue: (value: string) => void; onClose: () => void; safeZoneAnchor: HTMLInputElement | null; autoSuggestValue: string; }; const AutoSuggestPopup = forwardRef( ( { options, onSelect, focusedValue, setFocusedValue, onClose, safeZoneAnchor, autoSuggestValue, }, ref, ) => { return (
{options.map((group) => ( {group.options.map((item) => ( onSelect(item)} hasVirtualFocus={focusedValue === item.value} > {item.description && ( {item.description} )} {/* {item.tags && item.tags.length > 0 && (
{item.tags.map((tag) => ( {tag} ))}
)} */}
))}
))}
); }, ); function HighlightText({ text, highlightText, }: { text: string; highlightText: string; }) { if (!text || !highlightText) { return {text}; } if (text === highlightText) { return ; } const { noMatches, matches } = highlightSplit(text, highlightText); const highlighted: (string | JSX.Element)[] = []; noMatches.forEach((noMatch, idx) => { highlighted.push({noMatch}); if (matches && idx < matches.length) { highlighted.push(); } }); return {highlighted}; } function Highlight({ text }: { text: string }) { return {text}; } function highlightSplit(text: string, highlightText: string) { /* Skip loooong texts */ if (highlightText.length > 1000) { return { noMatches: [text], matches: null }; } /* Case insensitive filtering */ const filteringPattern = highlightText.replace( /[-[\]/{}()*+?.\\^$|]/g, "\\$&", ); const regexp = new RegExp(filteringPattern, "gi"); const noMatches = text.split(regexp); const matches = text.match(regexp); return { noMatches, matches }; } export { AutoSuggest };