Skip to content

Commit

Permalink
✨ feat: Add custom tab
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Aug 26, 2024
1 parent b9232c0 commit 4ceeb9c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 25 deletions.
23 changes: 23 additions & 0 deletions src/EmojiPicker/demos/CustomTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { EmojiPicker, Icon, Tooltip } from '@lobehub/ui';
import { Button } from 'antd';
import { SunIcon } from 'lucide-react';

const LOGO = 'https://registry.npmmirror.com/@lobehub/assets-logo/1.2.0/files/assets/logo-3d.webp';

export default () => (
<EmojiPicker
customTabs={[
{
label: (
<Tooltip title={'Custom'}>
<Icon icon={SunIcon} size={{ fontSize: 20, strokeWidth: 2.5 }} />
</Tooltip>
),
render: (handleAvatarChange) => (
<Button onClick={() => handleAvatarChange(LOGO)}>Custom Tab</Button>
),
value: 'custom',
},
]}
/>
);
4 changes: 4 additions & 0 deletions src/EmojiPicker/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ title: EmojiPicker

<code src="./demos/CustomEmoji.tsx" center></code>

## Custom Tabs

<code src="./demos/CustomTabs.tsx" center></code>

## APIs

<API></API>
75 changes: 50 additions & 25 deletions src/EmojiPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import data from '@emoji-mart/data';
import Picker from '@emoji-mart/react';
import { Popover } from 'antd';
import { Loader2Icon, SmileIcon, TrashIcon, UploadIcon } from 'lucide-react';
import { memo, useEffect, useRef, useState } from 'react';
import { CSSProperties, ReactNode, memo, useEffect, useRef, useState } from 'react';
import { Center, CenterProps, Flexbox } from 'react-layout-kit';
import useSWR from 'swr';
import useMergeState from 'use-merge-value';

import ActionIcon from '@/ActionIcon';
import Avatar from '@/Avatar';
import Icon from '@/Icon';
import TabsNav from '@/TabsNav';
import TabsNav, { TabsNavProps } from '@/TabsNav';
import Tooltip from '@/Tooltip';

import AvatarUploader, { type AvatarUploaderProps } from './AvatarUploader';
Expand All @@ -35,18 +35,27 @@ export interface CustomEmoji {
name: string;
}

export interface CustomTab {
label: ReactNode;
render: (handleAvatarChange: (avatar: string) => void) => ReactNode;
value: string;
}

export interface EmojiPickerProps extends Omit<CenterProps, 'onChange'> {
allowDelete?: boolean;
allowUpload?: boolean;
backgroundColor?: string;
compressSize?: number;
customEmojis?: CustomEmoji[];
customTabs?: CustomTab[];
defaultAvatar?: string;
loading?: boolean;
locale?: string;
onChange?: (emoji: string) => void;
onDelete?: () => void;
onUpload?: AvatarUploaderProps['onUpload'];
popupClassName?: string;
popupStyle?: CSSProperties;
size?: number;
texts?: AvatarUploaderProps['texts'] & {
delete?: string;
Expand Down Expand Up @@ -74,6 +83,9 @@ const EmojiPicker = memo<EmojiPickerProps>(
onClick,
onUpload,
className,
customTabs = [],
popupClassName,
popupStyle,
...rest
}) => {
const ref = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -111,39 +123,44 @@ const EmojiPicker = memo<EmojiPickerProps>(
return () => document.removeEventListener('click', handleClickOutside);
}, [active, open]);

const items: TabsNavProps['items'] = [
{
key: 'emoji',
label: (
<Tooltip title={texts?.emoji || 'Emoji'}>
<Icon icon={SmileIcon} size={{ fontSize: 20, strokeWidth: 2.5 }} />
</Tooltip>
),
},
allowUpload && {
key: 'upload',
label: (
<Tooltip title={texts?.upload || 'Upload'}>
<Icon icon={UploadIcon} size={{ fontSize: 20, strokeWidth: 2.5 }} />
</Tooltip>
),
},
...customTabs.map((tab) => ({ key: tab.value, label: tab.label })),
].filter(Boolean) as TabsNavProps['items'];

const showTabs = items && items.length > 1;

return (
<Popover
arrow={false}
content={
<Flexbox
className={styles.picker}
className={cx(styles.picker, popupClassName)}
onMouseEnter={() => setActive(true)}
onMouseLeave={() => setActive(false)}
ref={ref}
style={allowUpload ? { paddingTop: 4 } : {}}
style={{ minWidth: 310, paddingTop: showTabs ? 4 : 0, ...popupStyle }}
>
{allowUpload && (
{showTabs && (
<Flexbox align={'center'} horizontal justify={'space-between'} paddingInline={10}>
<TabsNav
activeKey={tab}
items={[
{
key: 'emoji',
label: (
<Tooltip title={texts?.emoji || 'Emoji'}>
<Icon icon={SmileIcon} size={{ fontSize: 20, strokeWidth: 2.5 }} />
</Tooltip>
),
},
{
key: 'upload',
label: (
<Tooltip title={texts?.upload || 'Upload'}>
<Icon icon={UploadIcon} size={{ fontSize: 20, strokeWidth: 2.5 }} />
</Tooltip>
),
},
]}
items={items}
onChange={(key) => setTab(key as any)}
variant={'compact'}
/>
Expand All @@ -167,7 +184,7 @@ const EmojiPicker = memo<EmojiPickerProps>(
i18n={i18n}
icons={'outline'}
locale={locale.split('-')[0]}
navPosition={allowUpload ? 'bottom' : 'top'}
navPosition={showTabs ? 'bottom' : 'top'}
onEmojiSelect={(e: any) => handleAvatarChange(e.src || e.native)}
previewPosition={'none'}
skinTonePosition={'none'}
Expand All @@ -182,11 +199,19 @@ const EmojiPicker = memo<EmojiPickerProps>(
texts={texts}
/>
)}
{customTabs.map(
(item) =>
tab === item.value && (
<Flexbox key={item.value} padding={10}>
{item.render(handleAvatarChange)}
</Flexbox>
),
)}
</Flexbox>
}
destroyTooltipOnHide={true}
open={open}
placement={'bottom'}
placement={'bottomLeft'}
rootClassName={styles.popover}
trigger={['click']}
>
Expand Down

0 comments on commit 4ceeb9c

Please sign in to comment.