perf: 修改dropdown的overlay到menu, 这是因为会被逐渐弃用

顺便修复了一些样式问题
pull/64/head
moonrailgun 2 years ago
parent f81d0386bb
commit 922f0ad229

@ -1,5 +1,5 @@
import { Icon } from 'tailchat-design';
import { Dropdown } from 'antd';
import { Dropdown, Popover } from 'antd';
import React, { useCallback, useState } from 'react';
import { useChatInputActionContext } from './context';
import { EmojiPanel } from '@/components/Emoji';
@ -21,10 +21,10 @@ export const ChatInputEmotion: React.FC = React.memo(() => {
const menu = <EmojiPanel onSelect={handleSelect} />;
return (
<Dropdown
visible={visible}
onVisibleChange={setVisible}
overlay={menu}
<Popover
open={visible}
onOpenChange={setVisible}
content={menu}
placement="topRight"
trigger={['click']}
>
@ -32,7 +32,7 @@ export const ChatInputEmotion: React.FC = React.memo(() => {
className="text-2xl cursor-pointer"
icon="mdi:emoticon-happy-outline"
/>
</Dropdown>
</Popover>
);
});
ChatInputEmotion.displayName = 'ChatInputEmotion';

@ -161,7 +161,7 @@ const NormalMessage: React.FC<ChatMessageItemProps> = React.memo((props) => {
</TcPopover>
<Dropdown
overlay={moreActions}
menu={moreActions}
placement="bottomLeft"
trigger={['click']}
onVisibleChange={setIsActionBtnActive}

@ -1,5 +1,5 @@
import { Icon } from 'tailchat-design';
import { Menu } from 'antd';
import { Menu, MenuProps } from 'antd';
import React, { useCallback } from 'react';
import {
ChatMessage,
@ -15,6 +15,7 @@ import {
import { openReconfirmModalP } from '@/components/Modal';
import copy from 'copy-to-clipboard';
import { getMessageTextDecorators } from '@/plugin/common';
import _compact from 'lodash/compact';
/**
*
@ -22,7 +23,7 @@ import { getMessageTextDecorators } from '@/plugin/common';
export function useChatMessageItemAction(
payload: ChatMessage,
options: { onClick?: () => void }
): React.ReactElement {
): MenuProps {
const context = useChatBoxContext();
const groupInfo = useGroupInfoContext();
const userInfo = useUserInfo();
@ -46,47 +47,33 @@ export function useChatMessageItemAction(
const isGroupOwner = groupInfo && groupInfo.owner === userInfo?._id; //
const isMessageAuthor = payload.author === userInfo?._id;
return (
<Menu onClick={options.onClick}>
<Menu.Item
key="copy"
icon={<Icon icon="mdi:content-copy" />}
onClick={handleCopy}
>
{t('复制')}
</Menu.Item>
{context.hasContext && (
<Menu.Item
key="reply"
icon={<Icon icon="mdi:reply" />}
onClick={() => sharedEvent.emit('replyMessage', payload)}
>
{t('回复')}
</Menu.Item>
)}
{(isGroupOwner || isMessageAuthor) && (
<Menu.Item
key="recall"
icon={<Icon icon="mdi:restore" />}
onClick={handleRecallMessage}
>
{t('撤回')}
</Menu.Item>
)}
{/* 仅群组管理员可见 */}
{isGroupOwner && (
<Menu.Item
key="delete"
danger={true}
icon={<Icon icon="mdi:delete-outline" />}
onClick={handleDeleteMessage}
>
{t('删除')}
</Menu.Item>
)}
</Menu>
);
return {
items: _compact([
{
key: 'copy',
label: t('复制'),
icon: <Icon icon="mdi:content-copy" />,
onClick: handleCopy,
},
context.hasContext && {
key: 'reply',
label: t('回复'),
icon: <Icon icon="mdi:reply" />,
onClick: () => sharedEvent.emit('replyMessage', payload),
},
(isGroupOwner || isMessageAuthor) && {
key: 'recall',
label: t('撤回'),
icon: <Icon icon="mdi:restore" />,
onClick: handleRecallMessage,
},
isGroupOwner && {
key: 'delete',
label: t('删除'),
danger: true,
icon: <Icon icon="mdi:delete-outline" />,
onClick: handleDeleteMessage,
},
] as MenuProps['items']),
};
}

@ -1,10 +1,15 @@
.emoji-picker {
.emoji-mart-anchors {
justify-content: flex-start;
@apply space-x-1;
.emoji-mart {
display: block;
.emoji-mart-anchor {
flex: initial;
.emoji-mart-anchors {
justify-content: flex-start;
@apply space-x-1;
.emoji-mart-anchor {
flex: initial;
}
}
}
}

@ -6,6 +6,8 @@ import { useGroupIdContext } from '@/context/GroupIdContext';
const { Option } = Select;
interface GroupPanelSelectorProps {
className?: string;
style?: React.CSSProperties;
value: string;
onChange: (value: string) => void;
groupId?: string;
@ -29,6 +31,8 @@ export const GroupPanelSelector: React.FC<GroupPanelSelectorProps> = React.memo(
return (
<Select
className={props.className}
style={props.style}
placeholder={t('请选择面板')}
value={props.value}
onChange={props.onChange}

@ -2,7 +2,7 @@ import { Icon } from 'tailchat-design';
import { openReconfirmModalP } from '@/components/Modal';
import { GroupUserPopover } from '@/components/popover/GroupUserPopover';
import { UserListItem } from '@/components/UserListItem';
import { Divider, Dropdown, Input, Menu, Skeleton } from 'antd';
import { Divider, Dropdown, Input, MenuProps, Skeleton } from 'antd';
import React, { useMemo } from 'react';
import {
formatFullTime,
@ -138,64 +138,65 @@ export const MembersPanel: React.FC<MembersPanelProps> = React.memo((props) => {
const hasMute = getMembersHasMute(members, member._id);
if (isGroupOwner) {
const menu: MenuProps = {
items: hasMute
? [
{
key: 'unmute',
label: t('解除禁言'),
onClick: () => handleUnmuteMember(member._id),
},
]
: [
{
key: 'mute',
label: t('禁言'),
children: [
{
key: '1m',
label: t('1分钟'),
onClick: () => handleMuteMember(member._id, 1 * 60 * 1000),
},
{
key: '5m',
label: t('5分钟'),
onClick: () => handleMuteMember(member._id, 5 * 60 * 1000),
},
{
key: '10m',
label: t('10分钟'),
onClick: () => handleMuteMember(member._id, 10 * 60 * 1000),
},
{
key: '30m',
label: t('30分钟'),
onClick: () => handleMuteMember(member._id, 30 * 60 * 1000),
},
{
key: '1d',
label: t('1天'),
onClick: () =>
handleMuteMember(member._id, 1 * 24 * 60 * 60 * 1000),
},
{
key: '7d',
label: t('7天'),
onClick: () =>
handleMuteMember(member._id, 7 * 24 * 60 * 60 * 1000),
},
{
key: '30d',
label: t('30天'),
onClick: () =>
handleMuteMember(member._id, 30 * 24 * 60 * 60 * 1000),
},
],
},
],
};
return (
<Dropdown
key={member._id}
trigger={['contextMenu']}
overlay={
<Menu>
{hasMute ? (
<Menu.Item onClick={() => handleUnmuteMember(member._id)}>
{t('解除禁言')}
</Menu.Item>
) : (
<Menu.SubMenu title={t('禁言')}>
<Menu.Item
onClick={() => handleMuteMember(member._id, 1 * 60 * 1000)}
>
{t('1分钟')}
</Menu.Item>
<Menu.Item
onClick={() => handleMuteMember(member._id, 5 * 60 * 1000)}
>
{t('5分钟')}
</Menu.Item>
<Menu.Item
onClick={() => handleMuteMember(member._id, 10 * 60 * 1000)}
>
{t('10分钟')}
</Menu.Item>
<Menu.Item
onClick={() => handleMuteMember(member._id, 30 * 60 * 1000)}
>
{t('30分钟')}
</Menu.Item>
<Menu.Item
onClick={() =>
handleMuteMember(member._id, 1 * 24 * 60 * 60 * 1000)
}
>
{t('1天')}
</Menu.Item>
<Menu.Item
onClick={() =>
handleMuteMember(member._id, 7 * 24 * 60 * 60 * 1000)
}
>
{t('7天')}
</Menu.Item>
<Menu.Item
onClick={() =>
handleMuteMember(member._id, 30 * 24 * 60 * 60 * 1000)
}
>
{t('30天')}
</Menu.Item>
</Menu.SubMenu>
)}
</Menu>
}
>
<Dropdown key={member._id} trigger={['contextMenu']} menu={menu}>
<div>
<UserListItem
userId={member._id}

@ -1,10 +1,10 @@
import React, { PropsWithChildren, useState } from 'react';
import { Dropdown } from 'antd';
import { Dropdown, MenuProps } from 'antd';
import { Icon } from 'tailchat-design';
import clsx from 'clsx';
interface SectionHeaderProps extends PropsWithChildren {
menu?: React.ReactElement;
menu?: MenuProps;
'data-testid'?: string;
}
@ -14,11 +14,11 @@ export const SectionHeader: React.FC<SectionHeaderProps> = React.memo(
return (
<div className="h-12 relative flex items-center py-0 text-base font-bold flex-shrink-0 thin-line-bottom">
{React.isValidElement(props.menu) ? (
{props.menu ? (
<Dropdown
className="overflow-hidden"
onVisibleChange={setVisible}
overlay={props.menu}
onOpenChange={setVisible}
menu={props.menu}
placement="topRight"
trigger={['click']}
>

@ -1,6 +1,6 @@
import { InviteCodeExpiredAt } from '@/components/InviteCodeExpiredAt';
import { generateInviteCodeUrl } from '@/utils/url-helper';
import { Menu, Typography, Dropdown } from 'antd';
import { Menu, Typography, Dropdown, MenuProps } from 'antd';
import React, { useState } from 'react';
import {
useAsyncRequest,
@ -38,16 +38,16 @@ export const CreateInviteCode: React.FC<CreateInviteCodeProps> = React.memo(
PERMISSION.core.unlimitedInvite,
]);
const menu = (
<Menu>
<Menu.Item
disabled={!hasUnlimitedInvitePermission}
onClick={() => handleCreateInviteLink(InviteCodeType.Permanent)}
>
{t('创建永久邀请码')}
</Menu.Item>
</Menu>
);
const menu: MenuProps = {
items: [
{
key: 'persist',
label: t('创建永久邀请码'),
disabled: !hasUnlimitedInvitePermission,
onClick: () => handleCreateInviteLink(InviteCodeType.Permanent),
},
],
};
return (
<div>
@ -72,7 +72,7 @@ export const CreateInviteCode: React.FC<CreateInviteCodeProps> = React.memo(
disabled={!hasInvitePermission}
loading={loading}
onClick={() => handleCreateInviteLink(InviteCodeType.Normal)}
overlay={menu}
menu={menu}
trigger={['click']}
>
{t('创建链接')}

@ -1,6 +1,7 @@
import React from 'react';
import { Menu } from 'antd';
import { Menu, MenuProps } from 'antd';
import _isNil from 'lodash/isNil';
import _compact from 'lodash/compact';
import {
PERMISSION,
useGroupInfo,
@ -29,25 +30,26 @@ export const GroupHeader: React.FC<GroupHeaderProps> = React.memo((props) => {
return null;
}
const menu = (
<Menu>
{showGroupDetail && (
<Menu.Item key="0" onClick={handleShowGroupDetail}>
{t('查看详情')}
</Menu.Item>
)}
{showInvite && (
<Menu.Item key="1" onClick={handleInviteUser}>
{t('邀请用户')}
</Menu.Item>
)}
<Menu.Item key="2" danger={true} onClick={handleQuitGroup}>
{t('退出群组')}
</Menu.Item>
</Menu>
);
const menu: MenuProps = {
items: _compact([
showGroupDetail && {
key: '0',
label: t('查看详情'),
onClick: handleShowGroupDetail,
},
showInvite && {
key: '1',
label: t('邀请用户'),
onClick: handleInviteUser,
},
{
key: '2',
label: t('退出群组'),
danger: true,
onClick: handleQuitGroup,
},
] as MenuProps['items']),
};
return (
<SectionHeader menu={menu} data-testid="group-header">

@ -12,7 +12,7 @@ import {
} from 'tailchat-shared';
import { GroupPanelItem } from '@/components/GroupPanelItem';
import { GroupTextPanelItem } from './TextPanelItem';
import { Dropdown, Menu } from 'antd';
import { Dropdown, Menu, MenuProps } from 'antd';
import copy from 'copy-to-clipboard';
import { usePanelWindow } from '@/hooks/usePanelWindow';
import { LoadingSpinner } from '@/components/LoadingSpinner';
@ -45,66 +45,64 @@ export const SidebarItem: React.FC<{
const isPinned =
isValidStr(groupInfo.pinnedPanelId) && groupInfo.pinnedPanelId === panelId;
const menu = (
<Menu
items={_compact([
{
key: 'copy',
label: t('复制链接'),
icon: <Icon icon="mdi:content-copy" />,
onClick: () => {
copy(`${location.origin}/main/group/${groupId}/${panelId}`);
showToasts(t('已复制到剪切板'));
},
},
{
key: 'new',
label: t('在新窗口打开'),
icon: <Icon icon="mdi:dock-window" />,
disabled: hasOpenedPanel,
onClick: openPanelWindow,
const menu: MenuProps = {
items: _compact([
{
key: 'copy',
label: t('复制链接'),
icon: <Icon icon="mdi:content-copy" />,
onClick: () => {
copy(`${location.origin}/main/group/${groupId}/${panelId}`);
showToasts(t('已复制到剪切板'));
},
isPinned
? {
key: 'unpin',
label: t('Unpin'),
icon: <Icon icon="mdi:pin-off" />,
onClick: () => {
dispatch(
groupActions.unpinGroupPanel({
groupId,
})
);
},
}
: {
key: 'pin',
label: t('Pin'),
icon: <Icon icon="mdi:pin" />,
onClick: () => {
dispatch(
groupActions.pinGroupPanel({
groupId,
panelId: panelId,
})
);
},
},
{
key: 'new',
label: t('在新窗口打开'),
icon: <Icon icon="mdi:dock-window" />,
disabled: hasOpenedPanel,
onClick: openPanelWindow,
},
isPinned
? {
key: 'unpin',
label: t('Unpin'),
icon: <Icon icon="mdi:pin-off" />,
onClick: () => {
dispatch(
groupActions.unpinGroupPanel({
groupId,
})
);
},
panel.type === GroupPanelType.TEXT && {
key: 'markAsRead',
label: t('标记为已读'),
icon: <Icon icon="mdi:message-badge-outline" />,
onClick: markConverseAllAck,
},
...extraMenuItems,
])}
/>
);
}
: {
key: 'pin',
label: t('Pin'),
icon: <Icon icon="mdi:pin" />,
onClick: () => {
dispatch(
groupActions.pinGroupPanel({
groupId,
panelId: panelId,
})
);
},
},
panel.type === GroupPanelType.TEXT && {
key: 'markAsRead',
label: t('标记为已读'),
icon: <Icon icon="mdi:message-badge-outline" />,
onClick: markConverseAllAck,
},
...extraMenuItems,
]),
};
const icon = isPinned ? <Icon icon="mdi:pin" /> : <Icon icon="mdi:pound" />;
return (
<Dropdown overlay={menu} trigger={['contextMenu']}>
<Dropdown menu={menu} trigger={['contextMenu']}>
<div>
{panel.type === GroupPanelType.TEXT ? (
<GroupTextPanelItem icon={icon} groupId={groupId} panel={panel} />

@ -86,17 +86,16 @@ export const FriendList: React.FC<{
</Tooltip>,
<div key="more">
<Dropdown
overlay={
<Menu>
<Menu.Item
key="delete"
danger={true}
onClick={() => handleRemoveFriend(friendId)}
>
{t('删除')}
</Menu.Item>
</Menu>
}
menu={{
items: [
{
key: 'delete',
danger: true,
onClick: () => handleRemoveFriend(friendId),
label: t('删除'),
},
],
}}
trigger={['click']}
placement="bottomRight"
>

@ -21,24 +21,22 @@ const GroupNavItem: React.FC<{ group: GroupInfo }> = React.memo(({ group }) => {
const hasUnread = useGroupUnread(groupId);
const { markGroupAllAck } = useGroupAck(groupId);
const menu = (
<Menu
items={[
{
key: 'ack',
label: t('标记为已读'),
icon: <Icon icon="mdi:message-badge-outline" />,
onClick: () => {
markGroupAllAck();
showSuccessToasts(t('已标记该群组所有消息已读'));
},
const menu = {
items: [
{
key: 'ack',
label: t('标记为已读'),
icon: <Icon icon="mdi:message-badge-outline" />,
onClick: () => {
markGroupAllAck();
showSuccessToasts(t('已标记该群组所有消息已读'));
},
]}
/>
);
},
],
};
return (
<Dropdown overlay={menu} trigger={['contextMenu']}>
<Dropdown menu={menu} trigger={['contextMenu']}>
<div>
<NavbarNavItem
name={group.name}

@ -62,6 +62,7 @@ export const AddGroupSubscribeModal: React.FC<{
}) => {
return (
<GroupPanelSelector
style={{ width: '100%' }}
value={props.value}
onChange={props.onChange}
groupId={groupId}

@ -51,6 +51,7 @@ export const AddGroupSubscribeModal: React.FC<{
}) => {
return (
<GroupPanelSelector
style={{ width: '100%' }}
value={props.value}
onChange={props.onChange}
groupId={groupId}

Loading…
Cancel
Save