feat: Add Mentions component (#16532)

* init

* first demo

* support empty

* add loading support

* add form sample

* update form sample

* omit value & defaultValue

* add 2 rest demo

* placement support

* update docs

* fix test

* update docs

* add test case

* fix lint

* follow textarea style

* update docs style
This commit is contained in:
zombieJ 2019-05-17 12:05:03 +08:00 committed by GitHub
parent 1635a1b018
commit ecb2eb6ede
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1058 additions and 11 deletions

View File

@ -34,6 +34,7 @@ Array [
"LocaleProvider",
"message",
"Menu",
"Mentions",
"Modal",
"Statistic",
"notification",

View File

@ -16,6 +16,7 @@ const renderEmpty = (componentName?: string): React.ReactNode => (
case 'TreeSelect':
case 'Cascader':
case 'Transfer':
case 'Mentions':
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} className={`${prefix}-small`} />;
default:
return <Empty />;

View File

@ -216,6 +216,7 @@ form {
}
}
.@{ant-prefix}-mentions,
textarea.@{ant-prefix}-input {
height: auto;
margin-bottom: 4px;

View File

@ -79,6 +79,8 @@ export { default as message } from './message';
export { default as Menu } from './menu';
export { default as Mentions } from './mentions';
export { default as Modal } from './modal';
export { default as Statistic } from './statistic';

View File

@ -1,14 +1,18 @@
---
category: Components
type: Data Entry
title: Mention
type: Deprecated
title: Mention (Deprecated)
---
Mention component.
Mention component. Deprecated, please use [Mentions](/components/mentions) instead.
## When To Use
## Why deprecated?
When need to mention someone or something.
<div class="ant-alert ant-alert-error ant-alert-no-icon">
Mention use
<a href="https://www.npmjs.com/package/draft-js" target="_blank" rel="noopener noreferrer">Draft.js</a>
to measure tips position, which use nearly 11.6% package size. We hope to reduce bundle size by using lightweight solution to handle this.
</div>
## API

View File

@ -3,6 +3,7 @@ import RcMention, { Nav, toString, toEditorState, getMentions } from 'rc-editor-
import { polyfill } from 'react-lifecycles-compat';
import classNames from 'classnames';
import Icon from '../icon';
import warning from '../_util/warning';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export type MentionPlacement = 'top' | 'bottom';
@ -56,6 +57,12 @@ class Mention extends React.Component<MentionProps, MentionState> {
filteredSuggestions: props.defaultSuggestions,
focus: false,
};
warning(
false,
'Mention',
'Mention component is deprecated. Please use Mentions component instead.',
);
}
onSearchChange = (value: string, prefix: string) => {

View File

@ -1,15 +1,19 @@
---
category: Components
subtitle: 提及
type: 数据录入
type: 废弃
subtitle: 提及(废弃)
title: Mention
---
提及组件。
提及组件。已废弃,请使用 [Mentions](/components/mentions) 代替。
## 何时使用
## 为何废弃?
用于在输入中提及某人或某事,常用于发布、聊天或评论功能。
<div class="ant-alert ant-alert-error ant-alert-no-icon">
Mention 组件使用了
<a href="https://www.npmjs.com/package/draft-js" target="_blank" rel="noopener noreferrer">Draft.js</a>
进行提示定位,占用了约 11.6% 的包大小。因而我们决定使用更轻量级的解决方案以便于在未来降低整个包的大小。
</div>
## API

View File

@ -0,0 +1,154 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/mentions/demo/async.md correctly 1`] = `
<div
class="ant-mentions"
style="width:100%"
>
<textarea
rows="1"
/>
</div>
`;
exports[`renders ./components/mentions/demo/basic.md correctly 1`] = `
<div
class="ant-mentions"
style="width:100%"
>
<textarea
rows="1"
>
@afc163
</textarea>
</div>
`;
exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
<form
class="ant-form ant-form-horizontal"
>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-6 ant-form-item-label"
>
<label
class=""
for="mention"
title="Top coders"
>
Top coders
</label>
</div>
<div
class="ant-col ant-col-16 ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control"
>
<span
class="ant-form-item-children"
>
<div
class="ant-mentions"
>
<textarea
data-__field="[object Object]"
data-__meta="[object Object]"
id="mention"
rows="3"
/>
</div>
</span>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-14 ant-col-offset-6 ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control"
>
<span
class="ant-form-item-children"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Submit
</span>
</button>
   
<button
class="ant-btn"
type="button"
>
<span>
Reset
</span>
</button>
</span>
</div>
</div>
</div>
</form>
`;
exports[`renders ./components/mentions/demo/placement.md correctly 1`] = `
<div
class="ant-mentions"
style="width:100%"
>
<textarea
rows="1"
/>
</div>
`;
exports[`renders ./components/mentions/demo/prefix.md correctly 1`] = `
<div
class="ant-mentions"
style="width:100%"
>
<textarea
placeholder="input @ to mention people, # to mention tag"
rows="1"
/>
</div>
`;
exports[`renders ./components/mentions/demo/readonly.md correctly 1`] = `
<div>
<div
style="margin-bottom:10px"
>
<div
class="ant-mentions ant-mentions-disabled"
style="width:100%"
>
<textarea
disabled=""
placeholder="this is disabled Mentions"
rows="1"
/>
</div>
</div>
<div
class="ant-mentions"
style="width:100%"
>
<textarea
placeholder="this is readOnly Mentions"
readonly=""
rows="1"
/>
</div>
</div>
`;

View File

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('mentions');

View File

@ -0,0 +1,83 @@
import React from 'react';
import { mount } from 'enzyme';
import Mentions from '..';
const { getMentions } = Mentions;
function simulateInput(wrapper, text = '', keyEvent) {
const lastChar = text[text.length - 1];
const myKeyEvent = keyEvent || {
which: lastChar.charCodeAt(0),
key: lastChar,
};
wrapper.find('textarea').simulate('keyDown', myKeyEvent);
const textareaInstance = wrapper.find('textarea').instance();
textareaInstance.value = text;
textareaInstance.selectionStart = text.length;
textareaInstance.selectionStart = text.length;
if (!keyEvent) {
wrapper.find('textarea').simulate('change', {
target: { value: text },
});
}
wrapper.find('textarea').simulate('keyUp', myKeyEvent);
wrapper.update();
}
describe('Mentions', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('getMentions', () => {
const mentions = getMentions('@light #bamboo cat', { prefix: ['@', '#'] });
expect(mentions).toEqual([
{
prefix: '@',
value: 'light',
},
{
prefix: '#',
value: 'bamboo',
},
]);
});
it('focus', () => {
const onFocus = jest.fn();
const onBlur = jest.fn();
const wrapper = mount(<Mentions onFocus={onFocus} onBlur={onBlur} />);
wrapper.find('textarea').simulate('focus');
expect(wrapper.find('.ant-mentions').hasClass('ant-mentions-focused')).toBeTruthy();
expect(onFocus).toHaveBeenCalled();
wrapper.find('textarea').simulate('blur');
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-mentions').hasClass('ant-mentions-focused')).toBeFalsy();
expect(onBlur).toHaveBeenCalled();
});
it('loading', () => {
const wrapper = mount(<Mentions loading />);
simulateInput(wrapper, '@');
expect(wrapper.find('.ant-mentions-dropdown-menu-item').length).toBe(1);
expect(wrapper.find('.ant-spin').length).toBeTruthy();
});
it('notFoundContent', () => {
const wrapper = mount(<Mentions notFoundContent={<span className="bamboo-light" />} />);
simulateInput(wrapper, '@');
expect(wrapper.find('.ant-mentions-dropdown-menu-item').length).toBe(1);
expect(wrapper.find('.bamboo-light').length).toBeTruthy();
});
});

View File

@ -0,0 +1,87 @@
---
order: 1
title:
zh-CN: 异步加载
en-US: Asynchronous loading
---
## zh-CN
匹配内容列表为异步返回时。
## en-US
async
```jsx
import { Mentions } from 'antd';
import debounce from 'lodash/debounce';
const { Option } = Mentions;
class AsyncMention extends React.Component {
constructor() {
super();
this.loadGithubUsers = debounce(this.loadGithubUsers, 800);
}
state = {
search: '',
loading: false,
users: [],
};
onSearch = search => {
this.setState({ search, loading: !!search, users: [] });
console.log('Search:', search);
this.loadGithubUsers(search);
};
loadGithubUsers(key) {
if (!key) {
this.setState({
users: [],
});
return;
}
fetch(`https://api.github.com/search/users?q=${key}`)
.then(res => res.json())
.then(({ items = [] }) => {
const { search } = this.state;
if (search !== key) return;
this.setState({
users: items.slice(0, 10),
loading: false,
});
});
}
render() {
const { users, loading } = this.state;
return (
<Mentions style={{ width: '100%' }} loading={loading} onSearch={this.onSearch}>
{users.map(({ login, avatar_url: avatar }) => (
<Option key={login} value={login} className="antd-demo-dynamic-option">
<img src={avatar} alt={login} />
<span>{login}</span>
</Option>
))}
</Mentions>
);
}
}
ReactDOM.render(<AsyncMention />, mountNode);
```
<style>
.antd-demo-dynamic-option img {
width: 20px;
height: 20px;
margin-right: 8px;
}
</style>

View File

@ -0,0 +1,42 @@
---
order: 0
title:
zh-CN: 基本使用
en-US: Basic
---
## zh-CN
基本使用
## en-US
Basic usage.
```jsx
import { Mentions } from 'antd';
const { Option } = Mentions;
function onChange(value) {
console.log('Change:', value);
}
function onSelect(option) {
console.log('select', option);
}
ReactDOM.render(
<Mentions
style={{ width: '100%' }}
onChange={onChange}
onSelect={onSelect}
defaultValue="@afc163"
>
<Option value="afc163">afc163</Option>
<Option value="zombieJ">zombieJ</Option>
<Option value="yesmeck">yesmeck</Option>
</Mentions>,
mountNode,
);
```

View File

@ -0,0 +1,81 @@
---
order: 2
title:
zh-CN: 配合 Form 使用
en-US: With Form
---
## zh-CN
受控模式,例如配合 Form 使用。
## en-US
Controlled mode, for example, to work with `Form`.
```jsx
import { Mentions, Form, Button } from 'antd';
const { Option, getMentions } = Mentions;
class App extends React.Component {
handleReset = e => {
e.preventDefault();
this.props.form.resetFields();
};
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((errors, values) => {
if (errors) {
console.log('Errors in the form!!!');
return;
}
console.log('Submit!!!');
console.log(values);
});
};
checkMention = (rule, value, callback) => {
const mentions = getMentions(value);
if (mentions.length < 2) {
callback(new Error('More than one must be selected!'));
} else {
callback();
}
};
render() {
const {
form: { getFieldDecorator },
} = this.props;
return (
<Form layout="horizontal">
<Form.Item label="Top coders" labelCol={{ span: 6 }} wrapperCol={{ span: 16 }}>
{getFieldDecorator('mention', {
rules: [{ validator: this.checkMention }],
})(
<Mentions rows="3">
<Option value="afc163">afc163</Option>
<Option value="zombieJ">zombieJ</Option>
<Option value="yesmeck">yesmeck</Option>
</Mentions>,
)}
</Form.Item>
<Form.Item wrapperCol={{ span: 14, offset: 6 }}>
<Button type="primary" onClick={this.handleSubmit}>
Submit
</Button>
&nbsp;&nbsp;&nbsp;
<Button onClick={this.handleReset}>Reset</Button>
</Form.Item>
</Form>
);
}
}
const FormDemo = Form.create()(App);
ReactDOM.render(<FormDemo />, mountNode);
```

View File

@ -0,0 +1,29 @@
---
order: 5
title:
zh-CN: 向上展开
en-US: Placement
---
## zh-CN
向上展开建议。
## en-US
Change the suggestions placement.
```jsx
import { Mentions } from 'antd';
const { Option } = Mentions;
ReactDOM.render(
<Mentions style={{ width: '100%' }} placement="top">
<Option value="afc163">afc163</Option>
<Option value="zombieJ">zombieJ</Option>
<Option value="yesmeck">yesmeck</Option>
</Mentions>,
mountNode,
);
```

View File

@ -0,0 +1,56 @@
---
order: 3
title:
zh-CN: 自定义触发字符
en-US: Customize Trigger Token
---
## zh-CN
通过 `prefix` 属性自定义触发字符。默认为 `@`, 可以定义为数组。
## en-US
Customize Trigger Token by `prefix` props. Default to `@`, `Array<string>` also supported.
```jsx
import { Mentions } from 'antd';
const { Option } = Mentions;
const MOCK_DATA = {
'@': ['afc163', 'zombiej', 'yesmeck'],
'#': ['1.0', '2.0', '3.0'],
};
class App extends React.Component {
state = {
prefix: '@',
};
onSearch = (_, prefix) => {
this.setState({ prefix });
};
render() {
const { prefix } = this.state;
return (
<Mentions
style={{ width: '100%' }}
placeholder="input @ to mention people, # to mention tag"
prefix={['@', '#']}
onSearch={this.onSearch}
>
{(MOCK_DATA[prefix] || []).map(value => (
<Option key={value} value={value}>
{value}
</Option>
))}
</Mentions>
);
}
}
ReactDOM.render(<App />, mountNode);
```

View File

@ -0,0 +1,45 @@
---
order: 4
title:
zh-CN: 无效或只读
en-US: disabled or readOnly
---
## zh-CN
通过 `disabled` 属性设置是否生效。通过 `readOnly` 属性设置是否只读。
## en-US
Configurate `disabled` and `readOnly`.
```jsx
import { Mentions } from 'antd';
const { Option } = Mentions;
function getOptions() {
return ['afc163', 'zombiej', 'yesmeck'].map(value => (
<Option key={value} value={value}>
{value}
</Option>
));
}
function App() {
return (
<div>
<div style={{ marginBottom: 10 }}>
<Mentions style={{ width: '100%' }} placeholder="this is disabled Mentions" disabled>
{getOptions()}
</Mentions>
</div>
<Mentions style={{ width: '100%' }} placeholder="this is readOnly Mentions" readOnly>
{getOptions()}
</Mentions>
</div>
);
}
ReactDOM.render(<App />, mountNode);
```

View File

@ -0,0 +1,54 @@
---
category: Components
type: Data Entry
title: Mentions
---
Mention component.
> Mention component is deprecated. Please click [here](/components/mention) to view old document.
## When To Use
When need to mention someone or something.
## API
```jsx
<Mentions onChange={onChange}>
<Mentions.Option value="sample">Sample</Mentions.Option>
</Mentions>
```
### Mention
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| autoFocus | Auto get focus when component mounted | boolean | `false` |
| defaultValue | Default value | string | - |
| filterOption | Customize filter option logic | false \| (input: string, option: OptionProps) => boolean | - |
| 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 | - |
| onChange | Trigger when value changed | (text: string) => void | - |
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - |
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - |
| onFocus | Trigger when mentions get focus | () => void | - |
| onBlur | Trigger when mentions lose focus | () => void | - |
### Mention methods
| Name | Description |
| ------- | ------------ |
| blur() | remove focus |
| focus() | get focus |
### Option
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| children | suggestion content | ReactNode | - |
| value | value of suggestion, the value will insert into input filed while selected | string | '' |

View File

@ -0,0 +1,164 @@
import classNames from 'classnames';
import omit from 'omit.js';
import * as React from 'react';
import { polyfill } from 'react-lifecycles-compat';
import RcMentions from 'rc-mentions';
import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
import Spin from '../spin';
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
const { Option } = RcMentions;
function loadingFilterOption() {
return true;
}
export type MentionPlacement = 'top' | 'bottom';
export interface OptionProps {
value: string;
children: React.ReactNode;
[key: string]: any;
}
export interface MentionProps extends RcMentionsProps {
loading?: boolean;
}
export interface MentionState {
focused: boolean;
}
interface MentionsConfig {
prefix?: string | string[];
split?: string;
}
interface MentionsEntity {
prefix: string;
value: string;
}
class Mentions extends React.Component<MentionProps, MentionState> {
static Option = Option;
static getMentions = (value: string = '', config?: MentionsConfig): MentionsEntity[] => {
const { prefix = '@', split = ' ' } = config || {};
const prefixList: string[] = Array.isArray(prefix) ? prefix : [prefix];
return value
.split(split)
.map(
(str = ''): MentionsEntity | null => {
let hitPrefix: string | null = null;
prefixList.some(prefixStr => {
const startStr = str.slice(0, prefixStr.length);
if (startStr === prefixStr) {
hitPrefix = prefixStr;
return true;
}
return false;
});
if (hitPrefix !== null) {
return {
prefix: hitPrefix,
value: str.slice(hitPrefix!.length),
};
}
return null;
},
)
.filter((entity): entity is MentionsEntity => !!entity && !!entity.value);
};
state = {
focused: false,
};
onFocus: React.FocusEventHandler<HTMLTextAreaElement> = (...args) => {
const { onFocus } = this.props;
if (onFocus) {
onFocus(...args);
}
this.setState({
focused: true,
});
};
onBlur: React.FocusEventHandler<HTMLTextAreaElement> = (...args) => {
const { onBlur } = this.props;
if (onBlur) {
onBlur(...args);
}
this.setState({
focused: false,
});
};
getNotFoundContent(renderEmpty: RenderEmptyHandler) {
const { notFoundContent } = this.props;
if (notFoundContent !== undefined) {
return notFoundContent;
}
return renderEmpty('Select');
}
getOptions = () => {
const { children, loading } = this.props;
if (loading) {
return (
<Option value={'ANTD_SEARCHING'} disabled>
<Spin size="small" />
</Option>
);
}
return children;
};
getFilterOption = () => {
const { filterOption, loading } = this.props;
if (loading) {
return loadingFilterOption;
}
return filterOption;
};
renderMentions = ({ getPrefixCls, renderEmpty }: ConfigConsumerProps) => {
const { focused } = this.state;
const { prefixCls: customizePrefixCls, className, disabled, ...restProps } = this.props;
const prefixCls = getPrefixCls('mentions', customizePrefixCls);
const mentionsProps = omit(restProps, ['loading']);
const mergedClassName = classNames(className, {
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: focused,
});
return (
<RcMentions
prefixCls={prefixCls}
notFoundContent={this.getNotFoundContent(renderEmpty)}
className={mergedClassName}
disabled={disabled}
{...mentionsProps}
filterOption={this.getFilterOption()}
onFocus={this.onFocus}
onBlur={this.onBlur}
>
{this.getOptions()}
</RcMentions>
);
};
render() {
return <ConfigConsumer>{this.renderMentions}</ConfigConsumer>;
}
}
polyfill(Mentions);
export default Mentions;

View File

@ -0,0 +1,64 @@
---
category: Components
subtitle: 提及
type: 数据录入
title: Mentions
---
提及组件。
> 原 Mention 组件已废弃,原文档请点击[这里](/components/mention)。
## 何时使用
用于在输入中提及某人或某事,常用于发布、聊天或评论功能。
## API
```jsx
<Mention
onChange={onChange}
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
/>
```
## API
```jsx
<Mentions onChange={onChange}>
<Mentions.Option value="sample">Sample</Mentions.Option>
</Mentions>
```
### Mention
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| autoFocus | 自动获得焦点 | boolean | `false` |
| defaultValue | 默认值 | string | - |
| filterOption | 自定义过滤逻辑 | false \| (input: string, option: OptionProps) => boolean | - |
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | 'Not Found' |
| placement | 弹出层展示位置 | 'top' \| 'bottom' | 'bottom' |
| prefix | 设置触发关键字 | string \| string[] | '@' |
| split | 设置选中项前后分隔符 | string | ' ' |
| validateSearch | 自定义触发验证逻辑 | (text: string, props: MentionsProps) => void | - |
| value | 设置值 | string | - |
| onChange | 值改变时触发 | (text: string) => void | - |
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - |
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - |
| onFocus | 获得焦点时触发 | () => void | - |
| onBlur | 失去焦点时触发 | () => void | - |
### Mention 方法
| 名称 | 描述 |
| ------- | -------- |
| blur() | 移除焦点 |
| focus() | 获取焦点 |
### Option
| 参数 | 说明 | 类型 | 默认值 |
| -------- | -------------- | --------- | ------ |
| children | 选项内容 | ReactNode | - |
| value | 选择时填充的值 | string | '' |

View File

@ -0,0 +1,156 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
.@{mention-prefix-cls} {
.reset-component;
.input;
position: relative;
display: inline-block;
height: auto;
white-space: pre-wrap;
padding: 0;
overflow: hidden;
vertical-align: bottom;
// =================== Status ===================
&-disabled {
> textarea {
.disabled();
}
}
&-focused {
.active();
}
// ================= Input Area =================
> textarea,
&-measure {
margin: 0;
padding: @input-padding-vertical-base @input-padding-horizontal-base;
overflow: inherit;
overflow-x: initial;
overflow-y: auto;
font-weight: inherit;
font-size: inherit;
font-family: inherit;
font-style: inherit;
font-variant: inherit;
font-size-adjust: inherit;
font-stretch: inherit;
line-height: inherit;
direction: inherit;
letter-spacing: inherit;
white-space: inherit;
text-align: inherit;
vertical-align: top;
word-wrap: break-word;
word-break: inherit;
tab-size: inherit;
}
> textarea {
width: 100%;
border: none;
outline: none;
resize: none;
&:read-only {
cursor: default;
}
}
&-measure {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
color: transparent;
pointer-events: none;
}
// ================== Dropdown ==================
&-dropdown {
// Ref select dropdown style
.reset-component;
position: absolute;
top: -9999px;
left: -9999px;
z-index: @zindex-dropdown;
box-sizing: border-box;
font-size: @font-size-base;
font-variant: initial;
background-color: @component-background;
border-radius: @border-radius-base;
outline: none;
box-shadow: @box-shadow-base;
&-hidden {
display: none;
}
&-menu {
max-height: 250px;
margin-bottom: 0;
padding-left: 0; // Override default ul/ol
overflow: auto;
list-style: none;
outline: none;
&-item {
position: relative;
display: block;
padding: 5px @control-padding-horizontal;
overflow: hidden;
color: @text-color;
font-weight: normal;
line-height: 22px;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
min-width: 100px;
transition: background 0.3s ease;
&:hover {
background-color: @item-hover-bg;
}
&:first-child {
border-radius: @border-radius-base @border-radius-base 0 0;
}
&:last-child {
border-radius: 0 0 @border-radius-base @border-radius-base;
}
&-disabled {
color: @disabled-color;
cursor: not-allowed;
&:hover {
color: @disabled-color;
background-color: @component-background;
cursor: not-allowed;
}
}
&-selected {
color: @text-color;
font-weight: @select-item-selected-font-weight;
background-color: @background-color-light;
}
&-active {
background-color: @item-active-bg;
}
}
}
}
}

View File

@ -0,0 +1,5 @@
import './index.less';
// style dependencies
import '../../empty/style';
import '../../spin/style';

View File

@ -42,6 +42,7 @@
"react-dom": ">=16.0.0"
},
"dependencies": {
"@ant-design/create-react-context": "^0.2.4",
"@ant-design/icons": "~2.0.0",
"@ant-design/icons-react": "~2.0.0",
"@types/react-slick": "^0.23.3",
@ -49,7 +50,6 @@
"babel-runtime": "6.x",
"classnames": "~2.2.6",
"copy-to-clipboard": "^3.0.8",
"@ant-design/create-react-context": "^0.2.4",
"css-animation": "^1.5.0",
"dom-closest": "^0.2.0",
"enquire.js": "^2.1.6",
@ -69,6 +69,7 @@
"rc-editor-mention": "^1.1.7",
"rc-form": "^2.4.0",
"rc-input-number": "~4.4.0",
"rc-mentions": "~0.3.1",
"rc-menu": "~7.4.12",
"rc-notification": "~3.3.0",
"rc-pagination": "~1.18.0",

View File

@ -54,6 +54,7 @@ module.exports = {
'Data Display': 4,
Feedback: 5,
Other: 6,
Deprecated: 7,
通用: 0,
布局: 1,
导航: 2,
@ -61,6 +62,7 @@ module.exports = {
数据展示: 4,
反馈: 5,
其他: 6,
废弃: 7,
},
docVersions: {
'0.9.x': 'http://09x.ant.design',

View File

@ -34,6 +34,7 @@ Array [
"LocaleProvider",
"message",
"Menu",
"Mentions",
"Modal",
"Statistic",
"notification",