has suggestions through hook

This commit is contained in:
Nate Kelley 2025-09-29 18:14:05 -06:00
parent 8864d97d5f
commit dfaa3d8c19
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
8 changed files with 31 additions and 61 deletions

View File

@ -132,7 +132,7 @@ const useUniqueSuggestions = (
const fourUniqueSuggestions = sampleSize(allSuggestions, 4); const fourUniqueSuggestions = sampleSize(allSuggestions, 4);
const items: MentionInputSuggestionsProps['suggestionItems'] = fourUniqueSuggestions.map( const items: MentionInputSuggestionsDropdownItem[] = fourUniqueSuggestions.map(
(suggestion) => ({ (suggestion) => ({
type: 'item', type: 'item',
value: suggestion.type + suggestion.value, value: suggestion.type + suggestion.value,
@ -148,7 +148,7 @@ const useUniqueSuggestions = (
addValueToInput: true, addValueToInput: true,
closeOnSelect: true, closeOnSelect: true,
}, },
]; ] satisfies MentionInputSuggestionsProps['suggestionItems'];
}, [suggestedPrompts]); }, [suggestedPrompts]);
}; };

View File

@ -1,4 +1,4 @@
import { Command } from 'cmdk'; import { Command, useCommandState } from 'cmdk';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useMemoizedFn } from '@/hooks/useMemoizedFn'; import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@ -51,7 +51,6 @@ export const MentionInputSuggestions = forwardRef<
) => { ) => {
const [hasClickedSelect, setHasClickedSelect] = useState(false); const [hasClickedSelect, setHasClickedSelect] = useState(false);
const [value, setValue] = useState(valueProp ?? defaultValue); const [value, setValue] = useState(valueProp ?? defaultValue);
const [hasResults, setHasResults] = useState(!!suggestionItems.length);
const commandListNavigatedRef = useRef(false); const commandListNavigatedRef = useRef(false);
const commandRef = useRef<HTMLDivElement>(null); const commandRef = useRef<HTMLDivElement>(null);
@ -106,7 +105,6 @@ export const MentionInputSuggestions = forwardRef<
onClick?.(); onClick?.();
if (closeSuggestionOnSelect) setHasClickedSelect(true); if (closeSuggestionOnSelect) setHasClickedSelect(true);
onSuggestionItemClick?.(params); onSuggestionItemClick?.(params);
setHasResults(false);
} }
); );
@ -153,14 +151,6 @@ export const MentionInputSuggestions = forwardRef<
[value] [value]
); );
function customFilter(value: string, search: string, keywords?: string[]): number {
console.log(value, search, keywords);
// Example: exact matches rank higher, case insensitive includes rank lower
if (value.toLowerCase() === search.toLowerCase()) return 2;
if (value.toLowerCase().includes(search.toLowerCase())) return 1;
return 0;
}
return ( return (
<Command <Command
ref={commandRef} ref={commandRef}
@ -186,26 +176,37 @@ export const MentionInputSuggestions = forwardRef<
/> />
{children && <div className="mt-3">{children}</div>} {children && <div className="mt-3">{children}</div>}
</MentionInputSuggestionsContainer> </MentionInputSuggestionsContainer>
{hasResults && <div className="border-b mb-1.5" />} <SuggestionsSeperator />
<MentionInputSuggestionsList <MentionInputSuggestionsList
show={showSuggestionList} show={showSuggestionList}
className={cn(suggestionsContainerClassName, hasResults && 'pb-1.5')} className={cn(suggestionsContainerClassName)}
> >
<MentionInputSuggestionsItemsSelector <MentionInputSuggestionsItemsSelector
suggestionItems={suggestionItems} suggestionItems={suggestionItems}
onSelect={onSelectItem} onSelect={onSelectItem}
addValueToInput={addSuggestionValueToInput} addValueToInput={addSuggestionValueToInput}
closeOnSelect={closeSuggestionOnSelect} closeOnSelect={closeSuggestionOnSelect}
hasResults={hasResults}
setHasResults={setHasResults}
/> />
<MentionInputSuggestionsEmpty <MentionInputSuggestionsEmpty emptyComponent={emptyComponent} />
setHasResults={setHasResults}
emptyComponent={emptyComponent}
/>
</MentionInputSuggestionsList> </MentionInputSuggestionsList>
</Command> </Command>
); );
} }
); );
const SuggestionsSeperator = () => {
const hasResults = useCommandState((x) => x.filtered.count) > 0;
if (!hasResults) return null;
return <div className="border-b mb-1.5" />;
};
const customFilter = (value: string, search: string, keywords?: string[]): number => {
if (keywords?.length) {
return keywords.includes(value) ? 2 : 0;
}
// Example: exact matches rank higher, case insensitive includes rank lower
if (value.toLowerCase() === search.toLowerCase()) return 2;
if (value.toLowerCase().includes(search.toLowerCase())) return 1;
return 0;
};

View File

@ -5,10 +5,8 @@ import { cn } from '@/lib/utils';
export const MentionInputSuggestionsEmpty = ({ export const MentionInputSuggestionsEmpty = ({
emptyComponent, emptyComponent,
setHasResults,
className, className,
}: { }: {
setHasResults: (hasResults: boolean) => void;
className?: string; className?: string;
emptyComponent: React.ReactNode; emptyComponent: React.ReactNode;
}) => { }) => {
@ -21,15 +19,6 @@ export const MentionInputSuggestionsEmpty = ({
)} )}
> >
{emptyComponent} {emptyComponent}
<SetHasResults setHasResults={setHasResults} />
</Command.Empty> </Command.Empty>
); );
}; };
const SetHasResults = ({ setHasResults }: { setHasResults: (hasResults: boolean) => void }) => {
useMount(() => {
setHasResults(false);
});
return null;
};

View File

@ -12,8 +12,6 @@ export type MentionInputSuggestionsGroupProps = MentionInputSuggestionsDropdownG
} & { } & {
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
hasResults: boolean;
setHasResults: (hasResults: boolean) => void;
}; };
export const MentionInputSuggestionsGroup = ({ export const MentionInputSuggestionsGroup = ({

View File

@ -1,4 +1,4 @@
import { Command } from 'cmdk'; import { Command, useCommandState } from 'cmdk';
import type React from 'react'; import type React from 'react';
import { useMount } from '@/hooks/useMount'; import { useMount } from '@/hooks/useMount';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@ -12,8 +12,6 @@ export type MentionInputSuggestionsItemProps = {
} & MentionInputSuggestionsDropdownItem & { } & MentionInputSuggestionsDropdownItem & {
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
hasResults: boolean;
setHasResults: (hasResults: boolean) => void;
}; };
export const MentionInputSuggestionsItem = ({ export const MentionInputSuggestionsItem = ({
@ -28,8 +26,6 @@ export const MentionInputSuggestionsItem = ({
type, type,
addValueToInput, addValueToInput,
onSelect, onSelect,
hasResults,
setHasResults,
className, className,
style, style,
}: MentionInputSuggestionsItemProps) => { }: MentionInputSuggestionsItemProps) => {
@ -42,6 +38,7 @@ export const MentionInputSuggestionsItem = ({
className className
)} )}
value={value} value={value}
data-testid={`type-${type}-value-${value}`}
style={style} style={style}
onSelect={() => { onSelect={() => {
onSelect({ onSelect({
@ -62,21 +59,6 @@ export const MentionInputSuggestionsItem = ({
</span> </span>
)} )}
{label} {label}
{!hasResults && <SetHasResults hasResults={hasResults} setHasResults={setHasResults} />}
</Command.Item> </Command.Item>
); );
}; };
const SetHasResults = ({
hasResults,
setHasResults,
}: {
hasResults: boolean;
setHasResults: (hasResults: boolean) => void;
}) => {
useMount(() => {
if (!hasResults) setHasResults(true);
});
return null;
};

View File

@ -16,8 +16,6 @@ export const MentionInputSuggestionsItemSelector = ({
}: { }: {
item: MentionInputSuggestionsProps['suggestionItems'][number]; item: MentionInputSuggestionsProps['suggestionItems'][number];
onSelect: (params: MentionInputSuggestionsOnSelectParams) => void; onSelect: (params: MentionInputSuggestionsOnSelectParams) => void;
hasResults: boolean;
setHasResults: (hasResults: boolean) => void;
} & GroupOverrideProps) => { } & GroupOverrideProps) => {
if (item.type === 'separator') { if (item.type === 'separator') {
return <MentionInputSuggestionsSeparator />; return <MentionInputSuggestionsSeparator />;
@ -44,8 +42,6 @@ export const MentionInputSuggestionsItemsSelector = ({
}: { }: {
suggestionItems: MentionInputSuggestionsProps['suggestionItems']; suggestionItems: MentionInputSuggestionsProps['suggestionItems'];
onSelect: (params: MentionInputSuggestionsOnSelectParams) => void; onSelect: (params: MentionInputSuggestionsOnSelectParams) => void;
hasResults: boolean;
setHasResults: (hasResults: boolean) => void;
} & GroupOverrideProps) => { } & GroupOverrideProps) => {
if (!suggestionItems) return null; if (!suggestionItems) return null;
return suggestionItems.map((item, index) => ( return suggestionItems.map((item, index) => (

View File

@ -1,4 +1,4 @@
import { Command } from 'cmdk'; import { Command, useCommandState } from 'cmdk';
import type React from 'react'; import type React from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@ -15,10 +15,12 @@ export const MentionInputSuggestionsList = ({
children, children,
show = true, show = true,
}: MentionInputSuggestionsListProps) => { }: MentionInputSuggestionsListProps) => {
const hasResults = useCommandState((x) => x.filtered.count) > 0;
if (!show) return null; if (!show) return null;
return ( return (
<Command.List className={cn('px-3', className)} style={style}> <Command.List className={cn('px-3', hasResults && 'pb-1.5', className)} style={style}>
{children} {children}
</Command.List> </Command.List>
); );

View File

@ -1,4 +1,4 @@
import { Command } from 'cmdk'; import { Command, useCommandState } from 'cmdk';
import type React from 'react'; import type React from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@ -6,6 +6,8 @@ export const MentionInputSuggestionsSeparator = ({
children, children,
...props ...props
}: React.ComponentPropsWithoutRef<typeof Command.Separator>) => { }: React.ComponentPropsWithoutRef<typeof Command.Separator>) => {
const hasResults = useCommandState((x) => x);
console.log(hasResults);
return ( return (
<Command.Separator className={cn('bg-border -mx-1 h-px my-1.5', props.className)} {...props}> <Command.Separator className={cn('bg-border -mx-1 h-px my-1.5', props.className)} {...props}>
{children} {children}