mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 17:44:35 +08:00
feat: mentions support status (#34071)
* test: skip form deps-lint * perf: remove useless has-feedback classname * test: raise test coverage * style: recover import arrangement
This commit is contained in:
parent
6a7acfbf6e
commit
65bf6550b1
@ -20562,7 +20562,7 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item ant-form-item-has-error"
|
||||
class="ant-row ant-form-item ant-form-item-has-feedback ant-form-item-has-error"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
|
||||
@ -20584,12 +20584,39 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions"
|
||||
class="ant-mentions-affix-wrapper ant-mentions-affix-wrapper-status-error ant-mentions-affix-wrapper-has-feedback"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="ant-mentions-feedback-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8553,7 +8553,7 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item ant-form-item-has-error"
|
||||
class="ant-row ant-form-item ant-form-item-has-feedback ant-form-item-has-error"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
|
||||
@ -8575,12 +8575,39 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions"
|
||||
class="ant-mentions-affix-wrapper ant-mentions-affix-wrapper-status-error ant-mentions-affix-wrapper-has-feedback"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="ant-mentions-feedback-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -155,7 +155,7 @@ ReactDOM.render(
|
||||
<Input.Password allowClear placeholder="with input password and allowClear" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Fail" validateStatus="error">
|
||||
<Form.Item label="Fail" validateStatus="error" hasFeedback>
|
||||
<Mentions />
|
||||
</Form.Item>
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
---
|
||||
order: 19
|
||||
title:
|
||||
zh-CN: 自定义校验
|
||||
en-US: Customized Validation
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
@ -206,3 +206,39 @@ Array [
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mentions/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-warning"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -206,3 +206,39 @@ Array [
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mentions/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-warning"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
51
components/mentions/demo/status.md
Normal file
51
components/mentions/demo/status.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
order: 8
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 Mentions 添加状态。可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to Mentions with `status`, which could be `error` or `warning`。
|
||||
|
||||
```jsx
|
||||
import { Mentions, Space } from 'antd';
|
||||
|
||||
const { Option } = Mentions;
|
||||
|
||||
function onChange(value) {
|
||||
console.log('Change:', value);
|
||||
}
|
||||
|
||||
function onSelect(option) {
|
||||
console.log('select', option);
|
||||
}
|
||||
|
||||
const MentionsStatuses = () => {
|
||||
const options = (
|
||||
<>
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Mentions onChange={onChange} onSelect={onSelect} defaultValue="@afc163" status="error">
|
||||
{options}
|
||||
</Mentions>
|
||||
<Mentions onChange={onChange} onSelect={onSelect} defaultValue="@afc163" status="warning">
|
||||
{options}
|
||||
</Mentions>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<MentionsStatuses />, mountNode);
|
||||
```
|
@ -21,32 +21,33 @@ When you need to mention someone or something.
|
||||
|
||||
### Mention
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocus | Auto get focus when component mounted | boolean | false |
|
||||
| autoSize | Textarea height autosize feature, can be set to true \| false or an object { minRows: 2, maxRows: 6 } | boolean \| object | false |
|
||||
| defaultValue | Default value | string | - |
|
||||
| filterOption | Customize filter option logic | false \| (input: string, option: OptionProps) => boolean | - |
|
||||
| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | - |
|
||||
| notFoundContent | Set mentions content when not match | ReactNode | `Not Found` |
|
||||
| placement | Set popup placement | `top` \| `bottom` | `bottom` |
|
||||
| prefix | Set trigger prefix keyword | string \| string\[] | `@` |
|
||||
| split | Set split string before and after selected mention | string | ` ` |
|
||||
| validateSearch | Customize trigger search logic | (text: string, props: MentionsProps) => void | - |
|
||||
| value | Set value of mentions | string | - |
|
||||
| onBlur | Trigger when mentions lose focus | () => void | - |
|
||||
| onChange | Trigger when value changed | (text: string) => void | - |
|
||||
| onFocus | Trigger when mentions get focus | () => void | - |
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - |
|
||||
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - |
|
||||
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoFocus | Auto get focus when component mounted | boolean | false | |
|
||||
| autoSize | Textarea height autosize feature, can be set to true \| false or an object { minRows: 2, maxRows: 6 } | boolean \| object | false | |
|
||||
| defaultValue | Default value | string | - | |
|
||||
| filterOption | Customize filter option logic | false \| (input: string, option: OptionProps) => boolean | - | |
|
||||
| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | - | |
|
||||
| notFoundContent | Set mentions content when not match | ReactNode | `Not Found` | |
|
||||
| placement | Set popup placement | `top` \| `bottom` | `bottom` | |
|
||||
| prefix | Set trigger prefix keyword | string \| string\[] | `@` | |
|
||||
| split | Set split string before and after selected mention | string | ` ` | |
|
||||
| status | Set validation status | 'error' \| 'warning' \| 'success' \| 'validating' | - | 4.19.0 |
|
||||
| validateSearch | Customize trigger search logic | (text: string, props: MentionsProps) => void | - | |
|
||||
| value | Set value of mentions | string | - | |
|
||||
| onBlur | Trigger when mentions lose focus | () => void | - | |
|
||||
| onChange | Trigger when value changed | (text: string) => void | - | |
|
||||
| onFocus | Trigger when mentions get focus | () => void | - | |
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - | |
|
||||
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - | |
|
||||
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - | |
|
||||
|
||||
### Mention methods
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| blur() | Remove focus |
|
||||
| focus() | Get focus |
|
||||
| Name | Description |
|
||||
| ------- | ------------ |
|
||||
| blur() | Remove focus |
|
||||
| focus() | Get focus |
|
||||
|
||||
### Option
|
||||
|
||||
|
@ -5,6 +5,13 @@ import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import Spin from '../spin';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import {
|
||||
getFeedbackIcon,
|
||||
getMergedStatus,
|
||||
getStatusClassNames,
|
||||
InputStatus,
|
||||
} from '../_util/statusUtils';
|
||||
|
||||
export const { Option } = RcMentions;
|
||||
|
||||
@ -22,6 +29,7 @@ export interface OptionProps {
|
||||
|
||||
export interface MentionProps extends RcMentionsProps {
|
||||
loading?: boolean;
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
export interface MentionState {
|
||||
@ -53,6 +61,7 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
filterOption,
|
||||
children,
|
||||
notFoundContent,
|
||||
status: customStatus,
|
||||
...restProps
|
||||
},
|
||||
ref,
|
||||
@ -61,6 +70,8 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
const innerRef = React.useRef<HTMLElement>();
|
||||
const mergedRef = composeRef(ref, innerRef);
|
||||
const { getPrefixCls, renderEmpty, direction } = React.useContext(ConfigContext);
|
||||
const { status: contextStatus, hasFeedback } = React.useContext(FormItemStatusContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const onFocus: React.FocusEventHandler<HTMLTextAreaElement> = (...args) => {
|
||||
if (restProps.onFocus) {
|
||||
@ -112,10 +123,11 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
[`${prefixCls}-focused`]: focused,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
!hasFeedback && className,
|
||||
);
|
||||
|
||||
return (
|
||||
const mentions = (
|
||||
<RcMentions
|
||||
prefixCls={prefixCls}
|
||||
notFoundContent={getNotFoundContent()}
|
||||
@ -131,6 +143,23 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
{getOptions()}
|
||||
</RcMentions>
|
||||
);
|
||||
|
||||
if (hasFeedback) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{mentions}
|
||||
{getFeedbackIcon(prefixCls, mergedStatus)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return mentions;
|
||||
};
|
||||
|
||||
const Mentions = React.forwardRef<unknown, MentionProps>(InternalMentions) as CompoundedComponent;
|
||||
|
@ -22,36 +22,37 @@ cover: https://gw.alipayobjects.com/zos/alicdn/jPE-itMFM/Mentions.svg
|
||||
|
||||
### Mentions
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocus | 自动获得焦点 | boolean | false |
|
||||
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false |
|
||||
| defaultValue | 默认值 | string | - |
|
||||
| filterOption | 自定义过滤逻辑 | false \| (input: string, option: OptionProps) => boolean | - |
|
||||
| getPopupContainer | 指定建议框挂载的 HTML 节点 | () => HTMLElement | - |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` |
|
||||
| placement | 弹出层展示位置 | `top` \| `bottom` | `bottom` |
|
||||
| prefix | 设置触发关键字 | string \| string\[] | `@` |
|
||||
| split | 设置选中项前后分隔符 | string | ` ` |
|
||||
| validateSearch | 自定义触发验证逻辑 | (text: string, props: MentionsProps) => void | - |
|
||||
| value | 设置值 | string | - |
|
||||
| onBlur | 失去焦点时触发 | () => void | - |
|
||||
| onChange | 值改变时触发 | (text: string) => void | - |
|
||||
| onFocus | 获得焦点时触发 | () => void | - |
|
||||
| onResize | resize 回调 | function({ width, height }) | - |
|
||||
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - |
|
||||
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoFocus | 自动获得焦点 | boolean | false | |
|
||||
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false | |
|
||||
| defaultValue | 默认值 | string | - | |
|
||||
| filterOption | 自定义过滤逻辑 | false \| (input: string, option: OptionProps) => boolean | - | |
|
||||
| getPopupContainer | 指定建议框挂载的 HTML 节点 | () => HTMLElement | - | |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` | |
|
||||
| placement | 弹出层展示位置 | `top` \| `bottom` | `bottom` | |
|
||||
| prefix | 设置触发关键字 | string \| string\[] | `@` | |
|
||||
| split | 设置选中项前后分隔符 | string | ` ` | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| validateSearch | 自定义触发验证逻辑 | (text: string, props: MentionsProps) => void | - | |
|
||||
| value | 设置值 | string | - | |
|
||||
| onBlur | 失去焦点时触发 | () => void | - | |
|
||||
| onChange | 值改变时触发 | (text: string) => void | - | |
|
||||
| onFocus | 获得焦点时触发 | () => void | - | |
|
||||
| onResize | resize 回调 | function({ width, height }) | - | |
|
||||
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - | |
|
||||
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - | |
|
||||
|
||||
### Mentions 方法
|
||||
|
||||
| 名称 | 描述 |
|
||||
| --- | --- |
|
||||
| blur() | 移除焦点 |
|
||||
| 名称 | 描述 |
|
||||
| ------- | -------- |
|
||||
| blur() | 移除焦点 |
|
||||
| focus() | 获取焦点 |
|
||||
|
||||
### Option
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| children | 选项内容 | ReactNode | - |
|
||||
| value | 选择时填充的值 | string | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| -------- | -------------- | --------- | ------ |
|
||||
| children | 选项内容 | ReactNode | - |
|
||||
| value | 选择时填充的值 | string | - |
|
||||
|
@ -1,6 +1,7 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
@import './status';
|
||||
|
||||
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
|
||||
|
||||
|
@ -3,3 +3,5 @@ import './index.less';
|
||||
// style dependencies
|
||||
import '../../empty/style';
|
||||
import '../../spin/style';
|
||||
|
||||
// deps-lint-skip: form
|
||||
|
42
components/mentions/style/status.less
Normal file
42
components/mentions/style/status.less
Normal file
@ -0,0 +1,42 @@
|
||||
@import '../../input/style/status';
|
||||
|
||||
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
|
||||
|
||||
.@{mention-prefix-cls} {
|
||||
&-status-error {
|
||||
.status-color(@mention-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
.status-color-common(@input-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.status-color(@mention-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
.status-color-common(@input-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
|
||||
&-affix-wrapper {
|
||||
position: relative;
|
||||
|
||||
.@{mention-prefix-cls}-feedback-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: @input-padding-horizontal-base;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&-status-error {
|
||||
.@{mention-prefix-cls}-feedback-icon {
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-has-warning {
|
||||
.@{mention-prefix-cls}-feedback-icon {
|
||||
color: @warning-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user