globall key press

This commit is contained in:
Nate Kelley 2025-10-06 15:36:49 -06:00
parent fd2e61808c
commit 7787a72061
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 62 additions and 30 deletions

View File

@ -33,7 +33,6 @@ export const MentionInputSuggestionsMentionsInput = forwardRef<
autoFocus={false}
disabled={props.disabled}
className="sr-only hidden h-0 border-0 p-0 pointer-events-none w-full"
// className="absolute -top-1 left-0 w-full h-full border border-red-500"
aria-hidden="true"
/>
</React.Fragment>

View File

@ -1,10 +1,11 @@
import { Command } from 'cmdk';
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { createContext, useContextSelector } from 'use-context-selector';
import { SearchEmptyState } from './SearchEmptyState';
import { SearchFooter } from './SearchFooter';
import { SearchInput } from './SearchInput';
import { SearchModalContentItems } from './SearchModalContentItems';
import type { SearchModalContentProps } from './search-modal.types';
import type { SearchItem, SearchModalContentProps } from './search-modal.types';
import { useViewSearchItem } from './useViewSearchItem';
export const SearchModalContent = <M, T extends string>({
@ -26,18 +27,40 @@ export const SearchModalContent = <M, T extends string>({
onViewSearchItem,
});
const [searchValue, setSearchValue] = useState<string>(defaulSearchValue);
const isCommandKeyPressedRef = useRef(false);
const onSearchChangePreflight = (searchValue: string) => {
setSearchValue(searchValue);
onSearchChange(searchValue);
};
const handleKeyDownGlobal = (e: React.KeyboardEvent) => {
if (e.metaKey || e.ctrlKey) {
isCommandKeyPressedRef.current = true;
}
handleKeyDown(e);
};
const handleKeyUpGlobal = (e: React.KeyboardEvent) => {
if (!e.metaKey && !e.ctrlKey) {
isCommandKeyPressedRef.current = false;
}
};
const onSelectGlobal = (item: SearchItem<M, T>) => {
if (item?.onSelect) {
item.onSelect();
}
onSelect(item, isCommandKeyPressedRef.current ? 'navigate' : 'select');
};
return (
<Command
className="min-w-[650px] min-h-[450px] max-h-[75vh] bg-background flex flex-col"
value={focusedValue}
onValueChange={setFocusedValue}
onKeyDown={handleKeyDown}
onKeyDown={handleKeyDownGlobal}
onKeyUp={handleKeyUpGlobal}
>
<SearchInput
searchValue={searchValue}
@ -48,7 +71,7 @@ export const SearchModalContent = <M, T extends string>({
<div className="border-b" />
<SearchModalContentItems
searchItems={searchItems}
onSelect={onSelect}
onSelectGlobal={onSelectGlobal}
onViewSearchItem={onViewSearchItem}
/>
<SearchEmptyState emptyState={emptyState} />

View File

@ -1,4 +1,4 @@
import { Command } from 'cmdk';
import { Command, useCommandState } from 'cmdk';
import { cn } from '@/lib/utils';
import type {
SearchItem,
@ -8,15 +8,24 @@ import type {
SearchModalContentProps,
} from './search-modal.types';
type CommonProps<M, T extends string> = {
onSelectGlobal: (d: SearchItem<M, T>) => void;
};
export const SearchModalContentItems = <M, T extends string>({
searchItems,
onSelect,
onViewSearchItem,
}: Pick<SearchModalContentProps<M, T>, 'searchItems' | 'onSelect' | 'onViewSearchItem'>) => {
onSelectGlobal,
}: Pick<SearchModalContentProps<M, T>, 'searchItems' | 'onViewSearchItem'> & CommonProps<M, T>) => {
const hasResults = useCommandState((x) => x.filtered.count) > 0;
return (
<Command.List className="flex flex-col overflow-y-auto flex-1">
<Command.List className={cn('flex flex-col overflow-y-auto flex-1', !hasResults && 'hidden')}>
{searchItems.map((item, index) => (
<ItemsSelecter key={keyExtractor(item, index)} item={item} />
<ItemsSelecter
key={keyExtractor(item, index)}
item={item}
onSelectGlobal={onSelectGlobal}
/>
))}
</Command.List>
);
@ -29,14 +38,20 @@ const keyExtractor = <M, T extends string>(item: SearchItems<M, T>, index: numbe
return item.type + index;
};
const ItemsSelecter = <M, T extends string>({ item }: { item: SearchItems<M, T> }) => {
const ItemsSelecter = <M, T extends string>({
item,
onSelectGlobal,
}: {
item: SearchItems<M, T>;
onSelectGlobal: (d: SearchItem<M, T>) => void;
}) => {
const type = item.type;
if (type === 'item') {
return <SearchItemComponent {...item} />;
return <SearchItemComponent {...item} onSelectGlobal={onSelectGlobal} />;
}
if (type === 'group') {
return <SearchItemGroupComponent item={item} />;
return <SearchItemGroupComponent item={item} onSelectGlobal={onSelectGlobal} />;
}
if (type === 'seperator') {
@ -48,16 +63,8 @@ const ItemsSelecter = <M, T extends string>({ item }: { item: SearchItems<M, T>
return null;
};
const SearchItemComponent = <M, T extends string>({
value,
label,
secondaryLabel,
tertiaryLabel,
icon,
onSelect,
loading,
disabled,
}: SearchItem<M, T>) => {
const SearchItemComponent = <M, T extends string>(item: SearchItem<M, T> & CommonProps<M, T>) => {
const { value, label, secondaryLabel, tertiaryLabel, icon, disabled, onSelectGlobal } = item;
return (
<Command.Item
className={cn(
@ -68,10 +75,7 @@ const SearchItemComponent = <M, T extends string>({
)}
value={value}
disabled={disabled}
onSelect={() => {
console.log('onSelect', value);
onSelect?.();
}}
onSelect={() => onSelectGlobal(item)}
>
{label}
</Command.Item>
@ -80,18 +84,24 @@ const SearchItemComponent = <M, T extends string>({
const SearchItemGroupComponent = <M, T extends string>({
item,
onSelectGlobal,
}: {
item: SearchItemGroup<M, T>;
onSelectGlobal: (d: SearchItem<M, T>) => void;
}) => {
return (
<Command.Group>
{item.items.map((item, index) => (
<ItemsSelecter key={keyExtractor(item, index)} item={item} />
<ItemsSelecter
key={keyExtractor(item, index)}
item={item}
onSelectGlobal={onSelectGlobal}
/>
))}
</Command.Group>
);
};
const SearchItemSeperatorComponent = ({ item }: { item: SearchItemSeperator }) => {
const SearchItemSeperatorComponent = ({ item: _item }: { item: SearchItemSeperator }) => {
return <Command.Separator className="border-t w-full" />;
};