Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support mentions onece delete #48389

Open
wants to merge 7 commits into
base: feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 28 additions & 0 deletions components/mentions/__tests__/index.test.tsx
Expand Up @@ -5,6 +5,7 @@ import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render } from '../../../tests/utils';
import KeyCode from 'rc-util/lib/KeyCode';

const { getMentions } = Mentions;

Expand Down Expand Up @@ -95,6 +96,33 @@ describe('Mentions', () => {
expect(textareaInstance.value).toEqual('');
});

it('should delete last item when Delete key is pressed', () => {
const { getByRole } = render(
<Mentions
style={{ width: '100%' }}
defaultValue="@afc163@zombieJ"
itemOnceDelete
options={[
{
value: 'afc163',
label: 'afc163',
},
{
value: 'zombieJ',
label: 'zombieJ',
},
{
value: 'yesmeck',
label: 'yesmeck',
},
]}
/>,
);
const textarea = getByRole('textbox') as HTMLTextAreaElement;
fireEvent.keyDown(textarea, { key: 'Delete', keyCode: KeyCode.BACKSPACE });
expect(textarea.value).toBe('@afc163');
});

it('should support custom clearIcon', () => {
const { container } = render(<Mentions allowClear={{ clearIcon: 'clear' }} />);
expect(container.querySelector('.ant-mentions-clear-icon')?.textContent).toBe('clear');
Expand Down
7 changes: 7 additions & 0 deletions components/mentions/demo/onceDelete.md
@@ -0,0 +1,7 @@
## zh-CN

一次删除按键删除。

## en-US

Once Delete.
27 changes: 27 additions & 0 deletions components/mentions/demo/onceDelete.tsx
@@ -0,0 +1,27 @@
import { Mentions } from 'antd';

const App: React.FC = () => {
return (
<Mentions

Check failure on line 5 in components/mentions/demo/onceDelete.tsx

View workflow job for this annotation

GitHub Actions / lint

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
style={{ width: '100%' }}
defaultValue="@afc163@zombieJ"
itemOnceDelete
options={[
{
value: 'afc163',
label: 'afc163',
},
{
value: 'zombieJ',
label: 'zombieJ',
},
{
value: 'yesmeck',
label: 'yesmeck',
},
]}
/>
);
};

export default App;
2 changes: 2 additions & 0 deletions components/mentions/index.en-US.md
Expand Up @@ -47,6 +47,7 @@ return (
<code src="./demo/allowClear.tsx">With clear icon</code>
<code src="./demo/autoSize.tsx">autoSize</code>
<code src="./demo/status.tsx">Status</code>
<code src="./demo/onceDelete.tsx">Once Delete</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>

Expand Down Expand Up @@ -79,6 +80,7 @@ Common props ref:[Common props](/docs/react/common-props)
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - | |
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - | |
| options | Option Configuration | [Options](#option) | \[] | 5.1.0 |
| itemOnceDelete | Delete Item with Once Delete | boolean | - | |

### Mention methods

Expand Down
19 changes: 19 additions & 0 deletions components/mentions/index.tsx
Expand Up @@ -22,6 +22,7 @@ import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
import Spin from '../spin';
import useStyle from './style';
import KeyCode from 'rc-util/lib/KeyCode';

export const { Option } = RcMentions;

Expand All @@ -45,6 +46,7 @@ export interface MentionProps extends Omit<RcMentionsProps, 'suffix'> {
status?: InputStatus;
options?: MentionsOptionProps[];
popupClassName?: string;
itemOnceDelete?: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 prop 不符合 antd 的 naming standard,可以先看看现有的 API 命名有没有可以复用的

/**
* @since 5.13.0
* @default "outlined"
Expand Down Expand Up @@ -75,13 +77,15 @@ const InternalMentions = React.forwardRef<MentionsRef, MentionProps>((props, ref
children,
notFoundContent,
options,
itemOnceDelete = false,
status: customStatus,
allowClear = false,
popupClassName,
style,
variant: customVariant,
...restProps
} = props;
const [data, setData] = React.useState<string>(props.value ?? props.defaultValue ?? '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useState 参数只有初始化会生效,不会同步 value,这么写不受控

const [focused, setFocused] = React.useState(false);
const innerRef = React.useRef<MentionsRef>(null);
const mergedRef = composeRef(ref, innerRef);
Expand Down Expand Up @@ -121,6 +125,18 @@ const InternalMentions = React.forwardRef<MentionsRef, MentionProps>((props, ref
setFocused(false);
};

const onChange = (value: string) => {
setData(value);
};

const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.keyCode === KeyCode.BACKSPACE && itemOnceDelete) {
if (data) {
setData(data.split('@').slice(0, -1).join('@'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ 可以自定义,不能写死 @。而且这个逻辑没考虑光标位置,行为是不正确的:

非 mention 对象不应该被删除

Kapture 2024-04-11 at 16 16 48

删除的 mention 对象不正确

Kapture 2024-04-11 at 16 20 00

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

working on it

}
}
};

const notFoundContentEle = React.useMemo<React.ReactNode>(() => {
if (notFoundContent !== undefined) {
return notFoundContent;
Expand Down Expand Up @@ -181,8 +197,11 @@ const InternalMentions = React.forwardRef<MentionsRef, MentionProps>((props, ref
allowClear={mergedAllowClear}
direction={direction}
style={{ ...contextMentions?.style, ...style }}
value={data}
{...restProps}
filterOption={mentionsfilterOption}
onKeyDown={onKeyDown}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

事件丢了,应该还需要透一下 props. onKeyDown?.(event)

onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
dropdownClassName={classNames(popupClassName, rootClassName, hashId, cssVarCls, rootCls)}
Expand Down
2 changes: 2 additions & 0 deletions components/mentions/index.zh-CN.md
Expand Up @@ -48,6 +48,7 @@ return (
<code src="./demo/allowClear.tsx">带移除图标</code>
<code src="./demo/autoSize.tsx">自动大小</code>
<code src="./demo/status.tsx">自定义状态</code>
<code src="./demo/onceDelete.tsx">一次删除</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>

Expand Down Expand Up @@ -80,6 +81,7 @@ return (
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - | |
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - | |
| options | 选项配置 | [Options](#option) | [] | 5.1.0 |
| itemOnceDelete | 支持一键清除 | boolean | false | | |

### Mentions 方法

Expand Down