search with key

This commit is contained in:
Nate Kelley 2025-10-06 15:07:27 -06:00
parent 0d98535936
commit fd2e61808c
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 60 additions and 4 deletions

View File

@ -31,6 +31,7 @@ const mockSearchItems: SearchItem[] = [
value: 'document-1',
keywords: ['document', 'file', 'pdf'],
type: 'item',
onSelect: fn(),
},
{
icon: '📊',
@ -39,6 +40,7 @@ const mockSearchItems: SearchItem[] = [
value: 'dashboard-1',
keywords: ['dashboard', 'analytics', 'charts'],
type: 'item',
onSelect: fn(),
},
...Array.from({ length: 10 }).map<SearchItem>((_, index) => ({
icon: '📊',
@ -47,6 +49,7 @@ const mockSearchItems: SearchItem[] = [
value: `testing-${index}`,
keywords: ['dashboard', 'analytics', 'charts'],
type: 'item' as const,
onSelect: fn(),
})),
];

View File

@ -4,7 +4,8 @@ import { SearchEmptyState } from './SearchEmptyState';
import { SearchFooter } from './SearchFooter';
import { SearchInput } from './SearchInput';
import { SearchModalContentItems } from './SearchModalContentItems';
import type { SearchItem, SearchModalContentProps } from './search-modal.types';
import type { SearchModalContentProps } from './search-modal.types';
import { useViewSearchItem } from './useViewSearchItem';
export const SearchModalContent = <M, T extends string>({
searchItems,
@ -20,6 +21,10 @@ export const SearchModalContent = <M, T extends string>({
secondaryContent,
openSecondaryContent,
}: SearchModalContentProps<M, T>) => {
const { handleKeyDown, focusedValue, setFocusedValue } = useViewSearchItem({
searchItems,
onViewSearchItem,
});
const [searchValue, setSearchValue] = useState<string>(defaulSearchValue);
const onSearchChangePreflight = (searchValue: string) => {
@ -30,7 +35,9 @@ export const SearchModalContent = <M, T extends string>({
return (
<Command
className="min-w-[650px] min-h-[450px] max-h-[75vh] bg-background flex flex-col"
value={searchValue}
value={focusedValue}
onValueChange={setFocusedValue}
onKeyDown={handleKeyDown}
>
<SearchInput
searchValue={searchValue}

View File

@ -69,6 +69,7 @@ const SearchItemComponent = <M, T extends string>({
value={value}
disabled={disabled}
onSelect={() => {
console.log('onSelect', value);
onSelect?.();
}}
>

View File

@ -6,7 +6,7 @@ export type SearchItem<M = unknown, T extends string = string> = {
value: T;
keywords?: string[];
meta?: M;
onSelect?: () => void;
onSelect?: () => void; //should only be used for side effects
loading?: boolean;
disabled?: boolean;
type: 'item';
@ -33,7 +33,7 @@ export type SearchModalContentProps<M = unknown, T extends string = string> = {
filterContent?: React.ReactNode;
searchItems: SearchItems<M, T>[];
onSearchChange: (searchValue: string) => void;
onSelect: (item: SearchItem<M, T>) => void;
onSelect: (item: SearchItem<M, T>, modifier: 'select' | 'navigate') => void;
onViewSearchItem: (item: SearchItem<M, T>) => void;
emptyState?: React.ReactNode | string;
placeholder?: string;

View File

@ -0,0 +1,45 @@
import { useState } from 'react';
import type { SearchItem, SearchItems } from './search-modal.types';
export const useViewSearchItem = <M, T extends string>({
searchItems,
onViewSearchItem,
}: {
searchItems: SearchItems<M, T>[];
onViewSearchItem: (item: SearchItem<M, T>) => void;
}) => {
const [focusedValue, setFocusedValue] = useState<string>('');
const handleKeyDown = (e: React.KeyboardEvent) => {
if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && onViewSearchItem) {
// Wait for next tick to get updated focusedValue
setTimeout(() => {
if (!focusedValue) return;
const findItem = (items: typeof searchItems): SearchItem<M, T> | null => {
for (const item of items) {
if (item.type === 'item' && item.value === focusedValue) {
return item;
}
if (item.type === 'group') {
const found = findItem(item.items);
if (found) return found;
}
}
return null;
};
const item = findItem(searchItems);
if (item) {
onViewSearchItem(item);
}
}, 0);
}
};
return {
handleKeyDown,
focusedValue,
setFocusedValue,
};
};