dropdown updates

This commit is contained in:
Nate Kelley 2025-03-03 16:53:00 -07:00
parent 284e708862
commit 56e31bd0d4
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 97 additions and 82 deletions

View File

@ -44,7 +44,7 @@ export const StatusDropdownContent: React.FC<{
}); });
}, [isAdmin, status, onChangeStatus]); }, [isAdmin, status, onChangeStatus]);
const onSelect = useMemoizedFn((item: DropdownItems[number]) => { const onSelect = useMemoizedFn((item: DropdownItem<VerificationStatus>) => {
const _item = item as DropdownItem<VerificationStatus>; const _item = item as DropdownItem<VerificationStatus>;
onChangeStatus(_item.value as VerificationStatus); onChangeStatus(_item.value as VerificationStatus);
}); });
@ -54,7 +54,7 @@ export const StatusDropdownContent: React.FC<{
emptyStateText="Nothing to see here..." emptyStateText="Nothing to see here..."
items={items} items={items}
onSelect={(v) => { onSelect={(v) => {
console.log(v); v;
}} }}
selectType="single" selectType="single"
menuHeader="Status"> menuHeader="Status">

View File

@ -38,7 +38,7 @@ export interface DropdownItem<T = string> {
disabled?: boolean; disabled?: boolean;
loading?: boolean; loading?: boolean;
selected?: boolean; selected?: boolean;
items?: DropdownItems; items?: DropdownItems<T>;
link?: string; link?: string;
linkIcon?: 'arrow-right' | 'arrow-external' | 'caret-right'; linkIcon?: 'arrow-right' | 'arrow-external' | 'caret-right';
} }
@ -50,7 +50,7 @@ export interface DropdownDivider {
export type DropdownItems<T = string> = (DropdownItem<T> | DropdownDivider | React.ReactNode)[]; export type DropdownItems<T = string> = (DropdownItem<T> | DropdownDivider | React.ReactNode)[];
export interface DropdownProps<T = string> extends DropdownMenuProps { export interface DropdownProps<T = string> extends DropdownMenuProps {
items: DropdownItems; items: DropdownItems<T>;
selectType?: 'single' | 'multiple' | 'none'; selectType?: 'single' | 'multiple' | 'none';
menuHeader?: string | React.ReactNode; //if string it will render a search box menuHeader?: string | React.ReactNode; //if string it will render a search box
closeOnSelect?: boolean; closeOnSelect?: boolean;
@ -68,8 +68,12 @@ const dropdownItemKey = (item: DropdownItems[number], index: number) => {
return `item-${index}`; return `item-${index}`;
}; };
export const Dropdown: React.FC<DropdownProps> = React.memo( export const Test = <T extends string = string>({}: { items: DropdownItems<T> }) => {
({ return <></>;
};
export const Dropdown = React.memo(
<T extends string>({
items, items,
selectType = 'none', selectType = 'none',
menuHeader, menuHeader,
@ -86,7 +90,7 @@ export const Dropdown: React.FC<DropdownProps> = React.memo(
footerContent, footerContent,
dir, dir,
modal modal
}) => { }: DropdownProps<T>) => {
const { filteredItems, searchText, handleSearchChange } = useDebounceSearch({ const { filteredItems, searchText, handleSearchChange } = useDebounceSearch({
items, items,
searchPredicate: (item, searchText) => { searchPredicate: (item, searchText) => {
@ -163,9 +167,11 @@ export const Dropdown: React.FC<DropdownProps> = React.memo(
if ((item as DropdownItem).value && !(item as DropdownItem).items) { if ((item as DropdownItem).value && !(item as DropdownItem).items) {
hotkeyIndex++; hotkeyIndex++;
} }
onSelect;
return ( return (
<DropdownItemSelector <DropdownItemSelector
item={item} item={item as DropdownItems<T>[number]}
index={hotkeyIndex} index={hotkeyIndex}
selectType={selectType} selectType={selectType}
onSelect={onSelect} onSelect={onSelect}
@ -182,9 +188,10 @@ export const Dropdown: React.FC<DropdownProps> = React.memo(
if ((item as DropdownItem).value && !(item as DropdownItem).items) { if ((item as DropdownItem).value && !(item as DropdownItem).items) {
hotkeyIndex++; hotkeyIndex++;
} }
return ( return (
<DropdownItemSelector <DropdownItemSelector
item={item} item={item as DropdownItems<T>[number]}
index={hotkeyIndex} index={hotkeyIndex}
selectType={selectType} selectType={selectType}
onSelect={onSelect} onSelect={onSelect}
@ -208,42 +215,47 @@ export const Dropdown: React.FC<DropdownProps> = React.memo(
Dropdown.displayName = 'Dropdown'; Dropdown.displayName = 'Dropdown';
const DropdownItemSelector: React.FC<{ const DropdownItemSelector = React.memo(
item: DropdownItems[number]; <T extends string>({
index: number; item,
onSelect: DropdownProps['onSelect']; index,
closeOnSelect: boolean; onSelect,
selectType: DropdownProps['selectType']; closeOnSelect,
}> = React.memo(({ item, index, onSelect, closeOnSelect, selectType }) => { selectType
if ((item as DropdownDivider).type === 'divider') { }: {
return <DropdownMenuSeparator />; item: DropdownItems<T>[number];
}
if (typeof item === 'object' && React.isValidElement(item)) {
return item;
}
return (
<DropdownItem
{...(item as DropdownItem)}
closeOnSelect={closeOnSelect}
onSelect={onSelect}
selectType={selectType}
index={index}
/>
);
});
DropdownItemSelector.displayName = 'DropdownItemSelector';
const DropdownItem: React.FC<
DropdownItem & {
onSelect: DropdownProps['onSelect'];
closeOnSelect: boolean;
index: number; index: number;
selectType: DropdownProps['selectType']; onSelect?: (value: T) => void;
closeOnSelect: boolean;
selectType: DropdownProps<T>['selectType'];
}) => {
if ((item as DropdownDivider).type === 'divider') {
return <DropdownMenuSeparator />;
}
if (typeof item === 'object' && React.isValidElement(item)) {
return item;
}
return (
<DropdownItem<T>
{...(item as DropdownItem<T>)}
closeOnSelect={closeOnSelect}
onSelect={onSelect}
selectType={selectType}
index={index}
/>
);
} }
> = ({ ) as <T extends string>(props: {
item: DropdownItems<T>[number];
index: number;
onSelect?: (value: T) => void;
closeOnSelect: boolean;
selectType: DropdownProps<T>['selectType'];
}) => JSX.Element;
const DropdownItem = <T extends string>({
label, label,
value, value,
showIndex, showIndex,
@ -262,6 +274,11 @@ const DropdownItem: React.FC<
truncate, truncate,
link, link,
linkIcon linkIcon
}: DropdownItem<T> & {
onSelect?: (value: T) => void;
closeOnSelect: boolean;
index: number;
selectType: DropdownProps<T>['selectType'];
}) => { }) => {
const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => { const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
if (onClick) onClick(); if (onClick) onClick();
@ -295,7 +312,6 @@ const DropdownItem: React.FC<
const renderContent = () => { const renderContent = () => {
const content = ( const content = (
<> <>
{showIndex && <span className="text-gray-light">{index}</span>}
{icon && !loading && <span className="text-icon-color">{icon}</span>} {icon && !loading && <span className="text-icon-color">{icon}</span>}
{loading && <CircleSpinnerLoader size={9} />} {loading && <CircleSpinnerLoader size={9} />}
<div className={cn('flex flex-col gap-y-1', truncate && 'overflow-hidden')}> <div className={cn('flex flex-col gap-y-1', truncate && 'overflow-hidden')}>
@ -310,6 +326,7 @@ const DropdownItem: React.FC<
linkIcon={linkIcon} linkIcon={linkIcon}
/> />
)} )}
{showIndex && <span className="text-gray-light ml-auto w-2 text-center">{index}</span>}
</> </>
); );
@ -372,41 +389,42 @@ const DropdownItem: React.FC<
); );
}; };
const DropdownSubMenuWrapper = React.memo( interface DropdownSubMenuWrapperProps<T extends string> {
({ items: DropdownItems<T> | undefined;
items, children: React.ReactNode;
children, closeOnSelect: boolean;
closeOnSelect, onSelect?: (value: T) => void;
onSelect, selectType: DropdownProps<T>['selectType'];
selectType }
}: {
items: DropdownItems | undefined; const DropdownSubMenuWrapper = <T extends string>({
children: React.ReactNode; items,
closeOnSelect: boolean; children,
onSelect?: DropdownProps['onSelect']; closeOnSelect,
selectType: DropdownProps['selectType']; onSelect,
}) => { selectType
return ( }: DropdownSubMenuWrapperProps<T>) => {
<DropdownMenuSub> return (
<DropdownMenuSubTrigger>{children}</DropdownMenuSubTrigger> <DropdownMenuSub>
<DropdownMenuPortal> <DropdownMenuSubTrigger>{children}</DropdownMenuSubTrigger>
<DropdownMenuSubContent> <DropdownMenuPortal>
{items?.map((item, index) => ( <DropdownMenuSubContent>
<DropdownItemSelector {items?.map((item, index) => (
key={dropdownItemKey(item, index)} <DropdownItemSelector<T>
item={item} key={dropdownItemKey(item, index)}
index={index} item={item}
onSelect={onSelect} index={index}
closeOnSelect={closeOnSelect} onSelect={onSelect}
selectType={selectType} closeOnSelect={closeOnSelect}
/> selectType={selectType}
))} />
</DropdownMenuSubContent> ))}
</DropdownMenuPortal> </DropdownMenuSubContent>
</DropdownMenuSub> </DropdownMenuPortal>
); </DropdownMenuSub>
} );
); };
DropdownSubMenuWrapper.displayName = 'DropdownSubMenuWrapper'; DropdownSubMenuWrapper.displayName = 'DropdownSubMenuWrapper';
const DropdownMenuHeaderSelector: React.FC<{ const DropdownMenuHeaderSelector: React.FC<{

View File

@ -1,9 +1,6 @@
import { MOCK_CHAT } from './MOCK_CHAT';
import { useChatIndividual } from './useChatIndividual'; import { useChatIndividual } from './useChatIndividual';
import { useMessageIndividual } from './useMessageIndividual'; import { useMessageIndividual } from './useMessageIndividual';
export * from './ChatProvider'; export * from './ChatProvider';
export { useChatIndividual, useMessageIndividual }; export { useChatIndividual, useMessageIndividual };
console.log(MOCK_CHAT);