set empty state

This commit is contained in:
Nate Kelley 2025-09-29 13:10:04 -06:00
parent e6531430f5
commit 109e2f6398
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 86 additions and 37 deletions

View File

@ -0,0 +1,6 @@
import React from 'react';
import { MentionInputSuggestions } from '@/components/ui/inputs/MentionInputSuggestions';
export const BusterChatInput = () => {
return <div>BusterChatInput</div>;
};

View File

@ -45,8 +45,9 @@ export const MentionInputSuggestions = ({
}: MentionInputSuggestionsProps) => { }: MentionInputSuggestionsProps) => {
const [hasClickedSelect, setHasClickedSelect] = useState(false); const [hasClickedSelect, setHasClickedSelect] = useState(false);
const [value, setValue] = useState(valueProp ?? defaultValue); const [value, setValue] = useState(valueProp ?? defaultValue);
const commandListNavigatedRef = useRef(false); const [hasResults, setHasResults] = useState(false);
const commandListNavigatedRef = useRef(false);
const commandRef = useRef<HTMLDivElement>(null); const commandRef = useRef<HTMLDivElement>(null);
const mentionsInputRef = useRef<MentionInputRef>(null); const mentionsInputRef = useRef<MentionInputRef>(null);
@ -83,6 +84,7 @@ export const MentionInputSuggestions = ({
onClick?.(); onClick?.();
if (closeSuggestionOnSelect) setHasClickedSelect(true); if (closeSuggestionOnSelect) setHasClickedSelect(true);
onSuggestionItemClick?.(params); onSuggestionItemClick?.(params);
setHasResults(false);
} }
); );
@ -135,6 +137,8 @@ export const MentionInputSuggestions = ({
value={value} value={value}
label={ariaLabel} label={ariaLabel}
className={cn('relative border rounded overflow-hidden bg-background shadow', className)} className={cn('relative border rounded overflow-hidden bg-background shadow', className)}
shouldFilter={shouldFilter}
filter={filter}
> >
<MentionInputSuggestionsContainer className={inputContainerClassName}> <MentionInputSuggestionsContainer className={inputContainerClassName}>
<MentionInputSuggestionsMentionsInput <MentionInputSuggestionsMentionsInput
@ -146,27 +150,30 @@ export const MentionInputSuggestions = ({
mentions={mentions} mentions={mentions}
value={value} value={value}
onChange={onChangeInputValue} onChange={onChangeInputValue}
shouldFilter={shouldFilter}
filter={filter}
onMentionItemClick={onMentionItemClick} onMentionItemClick={onMentionItemClick}
onPressEnter={onPressEnter || onSubmit} onPressEnter={onPressEnter || onSubmit}
commandListNavigatedRef={commandListNavigatedRef} commandListNavigatedRef={commandListNavigatedRef}
/> />
{children} {children}
</MentionInputSuggestionsContainer> </MentionInputSuggestionsContainer>
{hasResults && <div className="border-b mb-1.5" />}
<MentionInputSuggestionsList <MentionInputSuggestionsList
show={showSuggestionList} show={showSuggestionList}
className={suggestionsContainerClassName} className={cn(suggestionsContainerClassName, hasResults && 'pb-1.5')}
> >
<MentionInputSuggestionsItemsSelector <MentionInputSuggestionsItemsSelector
suggestionItems={suggestionItems} suggestionItems={suggestionItems}
onSelect={onSelectItem} onSelect={onSelectItem}
addValueToInput={addSuggestionValueToInput} addValueToInput={addSuggestionValueToInput}
closeOnSelect={closeSuggestionOnSelect} closeOnSelect={closeSuggestionOnSelect}
hasResults={hasResults}
setHasResults={setHasResults}
/>
<MentionInputSuggestionsEmpty
setHasResults={setHasResults}
emptyComponent={emptyComponent}
/> />
{emptyComponent && (
<MentionInputSuggestionsEmpty>{emptyComponent}</MentionInputSuggestionsEmpty>
)}
</MentionInputSuggestionsList> </MentionInputSuggestionsList>
</Command> </Command>
); );

View File

@ -1,17 +1,35 @@
import { Command } from 'cmdk'; import { Command } from 'cmdk';
import type React from 'react'; import type React from 'react';
import { useMount } from '@/hooks/useMount';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
export const MentionInputSuggestionsEmpty = ({ export const MentionInputSuggestionsEmpty = ({
children, emptyComponent,
...props setHasResults,
}: React.ComponentPropsWithoutRef<typeof Command.Empty>) => { className,
}: {
setHasResults: (hasResults: boolean) => void;
className?: string;
emptyComponent: React.ReactNode;
}) => {
return ( return (
<Command.Empty <Command.Empty
className={cn('text-gray-light py-6 text-center text-base', props.className)} className={cn(
{...props} 'text-gray-light py-6 text-center text-base',
!emptyComponent && 'hidden',
className
)}
> >
{children} {emptyComponent}
<SetHasResults setHasResults={setHasResults} />
</Command.Empty> </Command.Empty>
); );
}; };
const SetHasResults = ({ setHasResults }: { setHasResults: (hasResults: boolean) => void }) => {
useMount(() => {
setHasResults(false);
});
return null;
};

View File

@ -1,13 +1,19 @@
import { Command } from 'cmdk'; import { Command } from 'cmdk';
import type React from 'react'; import type React from 'react';
import { useMount } from '@/hooks/useMount';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import type { MentionInputSuggestionsDropdownItem, MentionInputSuggestionsOnSelectParams } from './MentionInputSuggestions.types'; import type {
MentionInputSuggestionsDropdownItem,
MentionInputSuggestionsOnSelectParams,
} from './MentionInputSuggestions.types';
export type MentionInputSuggestionsItemProps = { export type MentionInputSuggestionsItemProps = {
onSelect: (d: MentionInputSuggestionsOnSelectParams) => void; onSelect: (d: MentionInputSuggestionsOnSelectParams) => void;
} & MentionInputSuggestionsDropdownItem & { } & MentionInputSuggestionsDropdownItem & {
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
hasResults: boolean;
setHasResults: (hasResults: boolean) => void;
}; };
export const MentionInputSuggestionsItem = ({ export const MentionInputSuggestionsItem = ({
@ -23,6 +29,8 @@ export const MentionInputSuggestionsItem = ({
type, type,
addValueToInput, addValueToInput,
onSelect, onSelect,
hasResults,
setHasResults,
...props ...props
}: MentionInputSuggestionsItemProps) => { }: MentionInputSuggestionsItemProps) => {
return ( return (
@ -47,6 +55,21 @@ export const MentionInputSuggestionsItem = ({
}} }}
> >
{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

@ -1,7 +1,7 @@
import type { import type {
MentionInputSuggestionsProps,
MentionInputSuggestionsOnSelectParams,
GroupOverrideProps, GroupOverrideProps,
MentionInputSuggestionsOnSelectParams,
MentionInputSuggestionsProps,
} from './MentionInputSuggestions.types'; } from './MentionInputSuggestions.types';
import { MentionInputSuggestionsGroup } from './MentionInputSuggestionsGroup'; import { MentionInputSuggestionsGroup } from './MentionInputSuggestionsGroup';
import { MentionInputSuggestionsItem } from './MentionInputSuggestionsItem'; import { MentionInputSuggestionsItem } from './MentionInputSuggestionsItem';
@ -12,16 +12,19 @@ export const MentionInputSuggestionsItemSelector = ({
onSelect, onSelect,
addValueToInput, addValueToInput,
closeOnSelect, closeOnSelect,
...rest
}: { }: {
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 />;
} }
if (item.type === 'group') { if (item.type === 'group') {
return <MentionInputSuggestionsGroup {...item} onSelect={onSelect} />; return <MentionInputSuggestionsGroup {...item} onSelect={onSelect} {...rest} />;
} }
return ( return (
@ -30,28 +33,23 @@ export const MentionInputSuggestionsItemSelector = ({
onSelect={onSelect} onSelect={onSelect}
addValueToInput={item?.addValueToInput ?? addValueToInput} addValueToInput={item?.addValueToInput ?? addValueToInput}
closeOnSelect={item?.closeOnSelect ?? closeOnSelect} closeOnSelect={item?.closeOnSelect ?? closeOnSelect}
{...rest}
/> />
); );
}; };
export const MentionInputSuggestionsItemsSelector = ({ export const MentionInputSuggestionsItemsSelector = ({
suggestionItems, suggestionItems,
onSelect, ...rest
addValueToInput,
closeOnSelect,
}: { }: {
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) => (
<MentionInputSuggestionsItemSelector <MentionInputSuggestionsItemSelector key={keySelector(item, index)} item={item} {...rest} />
key={keySelector(item, index)}
item={item}
onSelect={onSelect}
addValueToInput={addValueToInput}
closeOnSelect={closeOnSelect}
/>
)); ));
}; };

View File

@ -16,8 +16,9 @@ export const MentionInputSuggestionsList = ({
show = true, show = true,
}: MentionInputSuggestionsListProps) => { }: MentionInputSuggestionsListProps) => {
if (!show) return null; if (!show) return null;
return ( return (
<Command.List className={cn('border-t px-3 py-1.5', className)} style={style}> <Command.List className={cn('px-3', className)} style={style}>
{children} {children}
</Command.List> </Command.List>
); );

View File

@ -6,13 +6,7 @@ import type { MentionInputSuggestionsProps } from './MentionInputSuggestions.typ
export type MentionInputSuggestionsMentionsInputProps = Pick< export type MentionInputSuggestionsMentionsInputProps = Pick<
MentionInputSuggestionsProps, MentionInputSuggestionsProps,
| 'mentions' 'mentions' | 'value' | 'placeholder' | 'defaultValue' | 'onMentionItemClick'
| 'value'
| 'placeholder'
| 'defaultValue'
| 'shouldFilter'
| 'filter'
| 'onMentionItemClick'
> & { > & {
onChange: MentionInputProps['onChange']; onChange: MentionInputProps['onChange'];
onPressEnter: MentionInputProps['onPressEnter']; onPressEnter: MentionInputProps['onPressEnter'];
@ -26,10 +20,11 @@ export type MentionInputSuggestionsMentionsInputProps = Pick<
export const MentionInputSuggestionsMentionsInput = forwardRef< export const MentionInputSuggestionsMentionsInput = forwardRef<
MentionInputRef, MentionInputRef,
MentionInputSuggestionsMentionsInputProps MentionInputSuggestionsMentionsInputProps
>(({ value: valueProp, placeholder, defaultValue, mentions, value, ...props }, ref) => { >(({ mentions, ...props }, ref) => {
const { value } = props;
return ( return (
<React.Fragment> <React.Fragment>
<MentionInput ref={ref} mentions={mentions} defaultValue={value} {...props} /> <MentionInput ref={ref} mentions={mentions} {...props} />
<Command.Input <Command.Input
value={value} value={value}
autoFocus={false} autoFocus={false}

View File

@ -0,0 +1 @@
export * from './MentionInputSuggestions';