Skip to content

Commit

Permalink
refactor: Add ConversationsItem.
Browse files Browse the repository at this point in the history
fix: Item tootip experience optimization
  • Loading branch information
YumoImer committed Aug 3, 2024
1 parent b447c2e commit d9307cd
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .dumi/preset/components-changelog-cn.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"Conversations":[],"Bubble":[]}
{"Conversations":[],"Bubble":[]}
2 changes: 1 addition & 1 deletion .dumi/preset/components-changelog-en.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"Conversations":[],"Bubble":[]}
{"Conversations":[],"Bubble":[]}
78 changes: 78 additions & 0 deletions components/conversations/Item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import { Tooltip, Typography, Dropdown } from 'antd';
import { MoreOutlined } from '@ant-design/icons';

import type { MenuProps } from 'antd';
import type { ConversationProps } from './interface';

interface ConversationsItemProps extends ConversationProps, React.HTMLAttributes<HTMLLIElement> {
classNames?: {
item?: string;
label?: string;
menu?: string;
};
styles?: {
item?: React.CSSProperties;
};
prefixCls?: string;
menu?: MenuProps;
}

const ConversationsItem: React.FC<ConversationsItemProps> = (props) => {
const {
classNames,
styles,
disabled,
onClick,
icon,
label,
key,
prefixCls,
menu,
} = props;

const [ellipsised, setEllipsised] = React.useState(false);

return (
<Tooltip title={ellipsised ? label : undefined} placement="right" mouseLeaveDelay={0}>
<li
className={classNames?.item}
style={styles?.item}
onClick={disabled ? undefined : onClick}
key={key}
>
{icon && <div className={`${prefixCls}-icon`}>
{icon}
</div>}
<Typography.Text
className={classNames?.label}
ellipsis={{
onEllipsis: (v) => {
setEllipsised(v);
},
}}
>
{label}
</Typography.Text>
{menu && !disabled &&
<Dropdown
menu={menu}
trigger={['click']}
disabled={disabled}
getPopupContainer={(triggerNode) => triggerNode.parentElement ?? document.body}
>
<MoreOutlined
onClick={(event) => {
event.stopPropagation();
setEllipsised(false);
}}
disabled={disabled}
className={classNames?.menu}
/>
</Dropdown>}
</li>
</Tooltip>
);
};

export default ConversationsItem;
95 changes: 32 additions & 63 deletions components/conversations/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useMemo } from 'react';
import { Divider, Dropdown, Typography } from 'antd';
import { MoreOutlined } from '@ant-design/icons';
import React from 'react';
import { Divider, Typography } from 'antd';
import classnames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import ConversationsItem from './Item';

import useStyle from './style';
import getPrefixCls from '../_util/getPrefixCls';
Expand All @@ -24,14 +24,14 @@ const Conversations: React.FC<ConversationsProps> = (props) => {
groupable,
} = props;

const prefixCls = getPrefixCls('conversations', customizePrefixCls);

const [mergedActiveKey, setMergedActiveKey] = useMergedState(defaultActiveKey, {
value: activeKey,
defaultValue: defaultActiveKey,
onChange: onActiveChange,
});

const prefixCls = getPrefixCls('conversations', customizePrefixCls);

const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);

const mergedCls = classnames(
Expand All @@ -42,23 +42,8 @@ const Conversations: React.FC<ConversationsProps> = (props) => {
styles?.list,
);

const makeItemProps = (item: ConversationProps) => ({
className: classnames(
classNames?.item,
`${prefixCls}-item`,
{ [`${prefixCls}-item-active`]: item.key === mergedActiveKey && !item.disabled },
{ [`${prefixCls}-item-disabled`]: item.disabled },
),
style: styles?.item,
menu: typeof menu === 'function' ? menu(item) : menu,
onClick: () => setMergedActiveKey(item.key),
});

const mergedLabelCls = classnames(`${prefixCls}-label`, classNames?.item);
const mergedMenuCls = classnames(`${prefixCls}-menu`, classNames?.item);

const groupedMap = data.reduce((map, item) => {
const { group = 'default' } = item;
const { group = '' } = item;

if (!Array.isArray(map[group])) {
map[group] = [];
Expand All @@ -69,44 +54,26 @@ const Conversations: React.FC<ConversationsProps> = (props) => {
return map;
}, {} as Record<GroupType, ConversationProps[]>);

function renderConversationItem(item: ConversationProps) {
const itemProps = makeItemProps(item);

return (
<li
className={itemProps.className}
style={itemProps.style}
onClick={item.disabled ? undefined : itemProps.onClick}
key={item.key}
>
{item.icon && <div className={`${prefixCls}-icon`}>
{item.icon}
</div>}
<Typography.Text
className={mergedLabelCls}
ellipsis={{ tooltip: item.label }}
>
{item.label}
</Typography.Text>
{menu && !item.disabled &&
<Dropdown
menu={itemProps.menu}
disabled={item.disabled}
getPopupContainer={(triggerNode) => triggerNode.parentElement ?? document.body}
>
<MoreOutlined
onClick={(event) => {
event.stopPropagation();
}}
disabled={item.disabled}
className={mergedMenuCls}
/>
</Dropdown>}
</li>
)
}

const conversationItems = useMemo(() => Object.keys(groupedMap).reduce(
const getItemProps = React.useCallback((item: ConversationProps) => ({
...item,
classNames: {
item: classnames(
classNames?.item,
`${prefixCls}-item`,
{ [`${prefixCls}-item-active`]: item.key === mergedActiveKey && !item.disabled },
{ [`${prefixCls}-item-disabled`]: item.disabled },
),
label: classnames(`${prefixCls}-label`, classNames?.item),
menu: `${prefixCls}-menu`,
},
prefixCls,
styles: styles,
menu: typeof menu === 'function' ? menu(item) : menu,
onClick: () => setMergedActiveKey(item.key),
}), [classNames, prefixCls, mergedActiveKey, styles, menu, setMergedActiveKey]);

// ============================ Render Items ============================
const convItems = React.useMemo(() => Object.keys(groupedMap).reduce(
(nodes, group) => {

if (groupable) {
Expand All @@ -129,15 +96,17 @@ const Conversations: React.FC<ConversationsProps> = (props) => {

return [
...nodes,
...groupedMap[group].map(renderConversationItem),
]
...groupedMap[group].map(item => React.createElement(ConversationsItem, getItemProps(item))),
];
},
[] as ReactNode[],
), [groupedMap, groupable]);
), [groupedMap, groupable, getItemProps]);


// ============================ Render ============================
return wrapCSSVar(
<ul className={mergedCls} style={styles?.list}>
{conversationItems}
{convItems}
</ul>,
);
};
Expand Down

0 comments on commit d9307cd

Please sign in to comment.