Merge pull request #18952 from ant-design/master-for-merge

chore: merge master into feature
This commit is contained in:
偏右 2019-09-23 16:06:48 +08:00 committed by GitHub
commit a5efb30b83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 701 additions and 469 deletions

View File

@ -4,7 +4,7 @@ on:
types: [published]
jobs:
publish:
compile:
runs-on: ubuntu-latest
steps:
@ -14,7 +14,7 @@ jobs:
- uses: actions/setup-node@v1
with:
node-version: '12.x'
registry-url: 'https://npm.pkg.github.com'
registry-url: 'https://registry.npmjs.org'
- name: install
run: npm install
@ -22,6 +22,54 @@ jobs:
- name: compile
run: npm run compile
- name: dist
run: npm run dist
- uses: actions/upload-artifact@master
with:
name: esm
path: es
- uses: actions/upload-artifact@master
with:
name: cjs
path: lib
- uses: actions/upload-artifact@master
with:
name: dist
path: dist
- uses: actions/upload-artifact@master
with:
name: package
path: package.json
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@master
with:
name: esm
- uses: actions/download-artifact@master
with:
name: cjs
- uses: actions/download-artifact@master
with:
name: dist
- uses: actions/download-artifact@master
with:
name: package
- uses: actions/setup-node@v1
with:
node-version: '12.x'
registry-url: 'https://npm.pkg.github.com'
- name: publish
run: npm publish
env:

View File

@ -15,6 +15,20 @@ timeline: true
---
## 3.23.4
`2019-09-21`
- 🐞 Fix item not disabled when Transfer is `disabled`. [#18849](https://github.com/ant-design/ant-design/pull/18849)
- 🐞 Revert Dragger to class component to fix ref warning. [#18707](https://github.com/ant-design/ant-design/issues/18707)
- 🐞 Fix Input `addonAfter` icon height bug in Chrome. [#18858](https://github.com/ant-design/ant-design/pull/18858)
- 🐞 Fix Menu lost state when being collapsed to `0px`. [#18907](https://github.com/ant-design/ant-design/pull/18907)
- 🐞 Disabled input should not trigger the action of suffix part. [#18900](https://github.com/ant-design/ant-design/pull/18900)
- 🐞 Fix title and content of Alert not break line when long text exist. [#18929](https://github.com/ant-design/ant-design/pull/18929)
- 💄 Add `@page-header-back-color` less variable. [#18887](https://github.com/ant-design/ant-design/pull/18887)
- TypeScript
- 🐞 Fix Table event type definition. [#18910](https://github.com/ant-design/ant-design/pull/18910)
## 3.23.3
`2019-09-16`

View File

@ -15,6 +15,20 @@ timeline: true
---
## 3.23.4
`2019-09-21`
- 🐞 修复 Transfer `disabled` 下勾选框不被禁用的问题。[#18849](https://github.com/ant-design/ant-design/pull/18849)
- 🐞 回滚 Dragger 到 class component 以修复 ref 警告信息。[#18707](https://github.com/ant-design/ant-design/issues/18707)
- 🐞 修复 Input `addonAfter` 里图标高度在 Chrome 下偏大的问题。[#18858](https://github.com/ant-design/ant-design/pull/18858)
- 🐞 修复 Menu 在 `collapsedWidth={0}` 时,折叠后丢失 `selectedKeys` 状态的问题。[#18907](https://github.com/ant-design/ant-design/pull/18907)
- 🐞 修复 Input 在禁用状态时,后缀图标可点击的问题。[#18900](https://github.com/ant-design/ant-design/pull/18900)
- 🐞 修复 Alert 标题和内容过长不换行的问题。[#18929](https://github.com/ant-design/ant-design/pull/18929)
- 💄 增加 `@page-header-back-color` less 变量。[#18887](https://github.com/ant-design/ant-design/pull/18887)
- TypeScript
- 🐞 修复 Table 事件的类型定义。[#18910](https://github.com/ant-design/ant-design/pull/18910)
## 3.23.3
`2019-09-16`

View File

@ -10,7 +10,7 @@
一套企业级 UI 设计语言和 React 组件库。
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design) [![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master) [![npm package](https://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd) [![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design) ![CI Status](https://github.com/ant-design/ant-design/workflows/Node%20CI/badge.svg) [![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master) [![](https://flat.badgen.net/npm/v/antd?icon=npm)](https://www.npmjs.com/package/antd) [![](https://badgen.net/npm/v/antd/next)](https://www.npmjs.com/package/antd) [![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design) [![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design?type=dev) [![Total alerts](https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/alerts/) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield) [![Issues need help](https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open)](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)

View File

@ -10,7 +10,7 @@
An enterprise-class UI design language and React UI library.
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design) [![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master) [![](https://flat.badgen.net/npm/v/antd?icon=npm)](https://www.npmjs.com/package/antd) [![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design) ![CI Status](https://github.com/ant-design/ant-design/workflows/Node%20CI/badge.svg) [![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master) [![](https://flat.badgen.net/npm/v/antd?icon=npm)](https://www.npmjs.com/package/antd) [![](https://badgen.net/npm/v/antd/next)](https://www.npmjs.com/package/antd) [![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design) [![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design?type=dev) [![Total alerts](https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/alerts/) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield) [![Issues need help](https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open)](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)

View File

@ -13,6 +13,7 @@
position: relative;
padding: 8px 15px 8px 37px;
word-wrap: break-word;
border-radius: @border-radius-base;
&&-no-icon {

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'omit.js';
import BreadcrumbItem from './BreadcrumbItem';
import BreadcrumbSeparator from './BreadcrumbSeparator';
import Menu from '../menu';
@ -144,6 +145,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
className,
routes,
children,
...restProps
} = this.props;
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
if (routes && routes.length > 0) {
@ -169,7 +171,11 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
});
}
return (
<div className={classNames(className, prefixCls)} style={style}>
<div
className={classNames(className, prefixCls)}
style={style}
{...omit(restProps, ['itemRender', 'params'])}
>
{crumbs}
</div>
);

View File

@ -102,4 +102,14 @@ describe('Breadcrumb', () => {
const wrapper = render(<Breadcrumb routes={routes} />);
expect(wrapper).toMatchSnapshot();
});
it('should support custom attribute', () => {
const wrapper = render(
<Breadcrumb data-custom="custom">
<Breadcrumb.Item data-custom="custom-item">xxx</Breadcrumb.Item>
<Breadcrumb.Item>yyy</Breadcrumb.Item>
</Breadcrumb>,
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -157,3 +157,36 @@ exports[`Breadcrumb should render a menu 1`] = `
</span>
</div>
`;
exports[`Breadcrumb should support custom attribute 1`] = `
<div
class="ant-breadcrumb"
data-custom="custom"
>
<span>
<span
class="ant-breadcrumb-link"
data-custom="custom-item"
>
xxx
</span>
<span
class="ant-breadcrumb-separator"
>
/
</span>
</span>
<span>
<span
class="ant-breadcrumb-link"
>
yyy
</span>
<span
class="ant-breadcrumb-separator"
>
/
</span>
</span>
</div>
`;

View File

@ -10,7 +10,7 @@ import ResponsiveObserve, {
responsiveArray,
} from '../_util/responsiveObserve';
const RowAligns = tuple('top', 'middle', 'bottom');
const RowAligns = tuple('top', 'middle', 'bottom', 'stretch');
const RowJustify = tuple('start', 'end', 'center', 'space-around', 'space-between');
export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
gutter?: number | Partial<Record<Breakpoint, number>>;

View File

@ -34,6 +34,11 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
};
onChange = () => {
const { disabled } = this.props;
if (disabled) {
return;
}
this.setState(({ visible }) => ({ visible: !visible }));
};

View File

@ -40,10 +40,10 @@ export default class Search extends React.Component<SearchProps, any> {
};
onSearch = (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLInputElement>) => {
const { onSearch, loading } = this.props;
if (loading) return;
const { onSearch, loading, disabled } = this.props;
if (loading || disabled) {
return;
}
if (onSearch) {
onSearch(this.input.input.value, e);
}

View File

@ -40,6 +40,13 @@ describe('Input.Password', () => {
expect(wrapper.find('.anticon-eye-invisible').length).toBe(1);
});
it('should not toggle visibility when disabled prop is true', () => {
const wrapper = mount(<Input.Password disabled />);
expect(wrapper.find('.anticon-eye-invisible').length).toBe(1);
wrapper.find('.anticon-eye-invisible').simulate('click');
expect(wrapper.find('.anticon-eye').length).toBe(0);
});
it('should keep focus state', () => {
const wrapper = mount(<Input.Password defaultValue="111" autoFocus />);
expect(document.activeElement).toBe(

View File

@ -31,6 +31,13 @@ describe('Input.Search', () => {
expect(wrapper.find('.ant-btn-primary[disabled]')).toHaveLength(1);
});
it('should disable search icon when disabled prop is true', () => {
const onSearch = jest.fn();
const wrapper = mount(<Search defaultValue="search text" onSearch={onSearch} disabled />);
wrapper.find('.anticon-search').simulate('click');
expect(onSearch).toHaveBeenCalledTimes(0);
});
it('should trigger onSearch when click search icon', () => {
const onSearch = jest.fn();
const wrapper = mount(<Search defaultValue="search text" onSearch={onSearch} />);

View File

@ -162,7 +162,6 @@
color: @input-color;
font-weight: normal;
font-size: @font-size-base;
line-height: 1;
text-align: center;
background-color: @input-addon-bg;
border: @border-width-base @border-style-base @input-border-color;
@ -395,6 +394,13 @@
}
}
.@{inputClass}-disabled ~ .@{inputClass}-suffix {
.anticon {
color: @disabled-color;
cursor: not-allowed;
}
}
.@{inputClass}-prefix {
left: @input-padding-horizontal-base + 1px;
}

View File

@ -643,4 +643,38 @@ describe('Menu', () => {
jest.useRealTimers();
});
// https://github.com/ant-design/ant-design/issues/18825
// https://github.com/ant-design/ant-design/issues/8587
it('should keep selectedKeys in state when collapsed to 0px', () => {
jest.useFakeTimers();
const wrapper = mount(
<Menu
mode="inline"
inlineCollapsed={false}
defaultSelectedKeys={['1']}
collapsedWidth={0}
openKeys={['3']}
>
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<Menu.SubMenu key="3" title="Option 3">
<Menu.Item key="4">Option 4</Menu.Item>
</Menu.SubMenu>
</Menu>,
);
expect(wrapper.find('.ant-menu-item-selected').getDOMNode().textContent).toBe('Option 1');
wrapper
.find('.ant-menu-item')
.at(1)
.simulate('click');
expect(wrapper.find('.ant-menu-item-selected').getDOMNode().textContent).toBe('Option 2');
wrapper.setProps({ inlineCollapsed: true });
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-menu-submenu-popup:not(.ant-menu-submenu-hidden)').length).toBe(0);
wrapper.setProps({ inlineCollapsed: false });
expect(wrapper.find('.ant-menu-item-selected').getDOMNode().textContent).toBe('Option 2');
jest.useRealTimers();
});
});

View File

@ -297,11 +297,11 @@ class InternalMenu extends React.Component<InternalMenuProps, MenuState> {
}
// https://github.com/ant-design/ant-design/issues/8587
if (
const hideMenu =
this.getInlineCollapsed() &&
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px')
) {
return null;
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
if (hideMenu) {
menuProps.openKeys = [];
}
return (

View File

@ -25,7 +25,7 @@
line-height: 1;
&-button {
.operation-unit();
color: #000;
color: @page-header-back-color;
cursor: pointer;
}
}

View File

@ -566,6 +566,7 @@
@page-header-padding: 24px;
@page-header-padding-vertical: 16px;
@page-header-padding-breadcrumb: 12px;
@page-header-back-color: #000;
// Breadcrumb
// ---

View File

@ -136,11 +136,11 @@ export interface TableCurrentDataSource<T> {
}
export interface TableEventListeners {
onClick?: (arg: React.SyntheticEvent) => void;
onDoubleClick?: (arg: React.SyntheticEvent) => void;
onContextMenu?: (arg: React.SyntheticEvent) => void;
onMouseEnter?: (arg: React.SyntheticEvent) => void;
onMouseLeave?: (arg: React.SyntheticEvent) => void;
onClick?: (arg: React.MouseEvent) => void;
onDoubleClick?: (arg: React.MouseEvent) => void;
onContextMenu?: (arg: React.MouseEvent) => void;
onMouseEnter?: (arg: React.MouseEvent) => void;
onMouseLeave?: (arg: React.MouseEvent) => void;
[name: string]: any; // https://github.com/ant-design/ant-design/issues/17245#issuecomment-504807714
}

View File

@ -59,6 +59,7 @@ export interface AbstractTooltipProps {
children?: React.ReactNode;
// align is a more higher api
align?: TooltipAlignConfig;
/** Internal. Hide tooltip when hidden. This will be renamed in future. */
destroyTooltipOnHide?: boolean;
}

View File

@ -1,5 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/tree-select/demo/async.md correctly 1`] = `
<span
aria-haspopup="listbox"
class="ant-select ant-select-enabled"
role="combobox"
style="width:300px"
tabindex="0"
>
<span
class="ant-select-selection ant-select-selection--single"
>
<span
class="ant-select-selection__rendered"
>
<span
class="ant-select-selection__placeholder"
>
Please select
</span>
</span>
<span
class="ant-select-arrow"
style="outline:none"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
</span>
</span>
</span>
`;
exports[`renders ./components/tree-select/demo/basic.md correctly 1`] = `
<span
aria-haspopup="listbox"

View File

@ -0,0 +1,79 @@
---
order: 5
title:
zh-CN: 异步加载
en-US: Asynchronous loading
---
## zh-CN
异步加载树节点。
## en-US
Asynchronous loading tree node.
```jsx
import { TreeSelect } from 'antd';
class Demo extends React.Component {
state = {
value: undefined,
treeData: [
{ id: 1, pId: 0, value: '1', title: 'Expand to load' },
{ id: 2, pId: 0, value: '2', title: 'Expand to load' },
{ id: 3, pId: 0, value: '3', title: 'Tree Node', isLeaf: true },
],
};
genTreeNode = (parentId, isLeaf = false) => {
const random = Math.random()
.toString(36)
.substring(2, 6);
return {
id: random,
pId: parentId,
value: random,
title: isLeaf ? 'Tree Node' : 'Expand to load',
isLeaf,
};
};
onLoadData = treeNode =>
new Promise(resolve => {
const { id } = treeNode.props;
setTimeout(() => {
this.setState({
treeData: this.state.treeData.concat([
this.genTreeNode(id, false),
this.genTreeNode(id, true),
]),
});
resolve();
}, 300);
});
onChange = value => {
console.log(value);
this.setState({ value });
};
render() {
const { treeData } = this.state;
return (
<TreeSelect
treeDataSimpleMode
style={{ width: 300 }}
value={this.state.value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
onChange={this.onChange}
loadData={this.onLoadData}
treeData={treeData}
/>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -4,8 +4,12 @@ import { UploadProps } from './interface';
export type DraggerProps = UploadProps & { height?: number };
const Dragger: React.FC<DraggerProps> = props => (
<Upload {...props} type="drag" style={{ ...props.style, height: props.height }} />
);
export default Dragger;
// stick class comoponent to avoid React ref warning inside Form
// https://github.com/ant-design/ant-design/issues/18707
// eslint-disable-next-line react/prefer-stateless-function
export default class Dragger extends React.Component<DraggerProps, any> {
render() {
const { props } = this;
return <Upload {...props} type="drag" style={{ ...props.style, height: props.height }} />;
}
}

View File

@ -23,7 +23,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v
| directory | support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | 3.7.0 |
| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. **Warningthis function is not supported in IE9**。 | (file, fileList) => `boolean | Promise` | - | |
| customRequest | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | Function | - | |
| data | Uploading params or function which can return uploading params. | object\|function(file) | - | |
| data | Uploading extra params or function which can return uploading extra params. | object\|function(file) | - | |
| defaultFileList | Default list of files that have been uploaded. | object\[] | - | |
| disabled | disable upload button | boolean | false | |
| fileList | List of files that have been uploaded (controlled). Here is a common issue [#2423](https://github.com/ant-design/ant-design/issues/2423) when using it | object\[] | - | |

View File

@ -24,7 +24,7 @@ title: Upload
| directory | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory) | boolean | false | 3.7.0 |
| beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象Promise 对象 reject 时则停止上传resolve 时开始上传( resolve 传入 `File``Blob` 对象则上传 resolve 传入对象)。**注意IE9 不支持该方法**。 | (file, fileList) => `boolean | Promise` | 无 | |
| customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | Function | 无 | |
| data | 上传所需参数或返回上传参数的方法 | object\|(file) => object | 无 | |
| data | 上传所需额外参数或返回上传额外参数的方法 | object\|(file) => object | 无 | |
| defaultFileList | 默认已经上传的文件列表 | object\[] | 无 | |
| disabled | 是否禁用 | boolean | false | |
| fileList | 已经上传的文件列表(受控),使用此参数时,如果遇到 `onChange` 只调用一次的问题,请参考 [#2423](https://github.com/ant-design/ant-design/issues/2423) | object\[] | 无 | |

View File

@ -20,8 +20,10 @@ title: 概览
- **模板:** 页面级示例,启发用户如何通过组件搭建出系统中的典型页面,如:详情页。
- **组件**
- 基础组件:构成系统最基础的元素,如按钮、分页器。
- 业务组件/模块:区块级示例,一般由多个组件构成,如 PageHeader 通用页头。
- **通用概念:** 一些保证 ETC 体系化的约定排版、文案、Action Placement、必填选项。
- 业务组件/区块:区块级示例,一般由多个组件构成,如 PageHeader 通用页头;在技术实现上,可能是代码片段,也可能是组件,也可能是先代码片段后变成组件。
- **通用概念:**
- Global Style通过 Design Token 的方式控制视觉表达,比如:字体、颜色、圆角、阴影等。
- Guide一些保证 ETC 体系化的约定排版、文案、Action Placement、必填选项。
## 资源

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "3.23.3",
"version": "3.23.4",
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",
@ -141,7 +141,7 @@
"warning": "~4.0.3"
},
"devDependencies": {
"@ant-design/colors": "^3.1.0",
"@ant-design/colors": "^3.2.2",
"@ant-design/tools": "^8.0.4",
"@packtracker/webpack-plugin": "^2.0.1",
"@sentry/browser": "^5.4.0",
@ -164,7 +164,7 @@
"bisheng-plugin-toc": "^0.4.4",
"bundlesize": "^0.18.0",
"chalk": "^2.4.2",
"cross-env": "^5.2.0",
"cross-env": "^6.0.0",
"css-split-webpack-plugin": "^0.2.6",
"dekko": "^0.2.1",
"docsearch.js": "^2.6.3",
@ -201,6 +201,7 @@
"prettier": "^1.17.1",
"pretty-quick": "^1.11.0",
"querystring": "^0.2.0",
"rc-footer": "^0.5.0",
"rc-queue-anim": "^1.6.12",
"rc-scroll-anim": "^2.5.8",
"rc-tween-one": "^2.4.1",

View File

@ -54,6 +54,7 @@ module.exports = {
'app.footer.awesome': 'Awesome Ant Design',
'app.footer.course': 'Ant Design Practical Tutorial',
'app.footer.chinamirror': 'China Mirror 🇨🇳',
'app.footer.primary-color-changing': 'Changing primary color...',
'app.footer.primary-color-changed': 'Change primary color successfully!',
'app.footer.scaffold': 'Scaffold',
'app.footer.kitchen': 'Sketch Toolkit',
@ -76,8 +77,8 @@ module.exports = {
'app.footer.feedback': 'Feedback',
'app.footer.stackoverflow': 'StackOverflow',
'app.footer.segmentfault': 'SegmentFault',
'app.footer.discuss-en': 'Chat Room (English)',
'app.footer.discuss-cn': 'Chat Room (中文)',
'app.footer.discuss-en': 'Chat Room 🇺🇸',
'app.footer.discuss-cn': 'Chat Room 🇨🇳',
'app.footer.bug-report': 'Bug Report',
'app.footer.issues': 'Issues',
'app.footer.version': 'Version: ',

View File

@ -1,86 +1,17 @@
@import '~rc-footer/assets/index.css';
@import './colors';
@padding-space: 40px;
footer {
position: relative;
z-index: 9;
clear: both;
margin-left: -1px;
color: rgba(255, 255, 255, 0.4);
font-size: 14px;
background-color: #000;
.footer-wrap {
position: relative;
padding: 60px @padding-space;
text-align: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
.footer-center {
display: inline-block;
text-align: left;
> h2 {
position: relative;
margin: 0 auto 24px;
font-weight: 500;
font-size: 16px;
> .title-icon {
width: 27px;
margin-right: 16px;
}
> .anticon {
position: absolute;
top: 3px;
left: -22px;
color: #aaa;
font-size: 16px;
}
}
> div {
margin: 12px 0;
> span {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
}
}
.rc-footer {
&-container {
max-width: unset;
padding: 60px;
}
.bottom-bar {
margin: 0;
padding: 16px @padding-space;
overflow: hidden;
font-size: 16px;
font-family: Avenir, @font-family, sans-serif;
line-height: 32px;
text-align: center;
&-bottom-container {
max-width: calc(100% - 120px);
}
a {
margin-left: 4px;
color: rgba(255, 255, 255, 0.65);
&:hover {
color: #fff;
}
}
.translate-button {
text-align: left;
}
.heart {
color: #f73f51;
font-size: 22px;
}
}
a {
color: rgba(255, 255, 255, 0.9);
}
h2 {
color: rgba(255, 255, 255, 1);
& > span {
color: rgba(255, 255, 255, 1);
}
&-columns {
justify-content: space-between;
}
}

View File

@ -151,31 +151,6 @@
border-radius: 0;
}
#footer {
text-align: center;
.footer-wrap {
padding: 40px;
.ant-row {
padding: 0;
> div {
&:nth-child(2),
&:nth-child(4) {
display: none;
}
a {
font-weight: 300;
}
}
}
}
.footer-center {
text-align: center;
}
h2 {
margin-top: 16px;
}
}
.prev-next-nav {
width: ~'calc(100% - 32px)';
margin-left: 16px;

View File

@ -81,7 +81,7 @@ class Article extends React.Component {
return (
/* eslint-disable-next-line */
<article className="markdown" onClick={this.onResourceClick}>
<Helmet>
<Helmet encodeSpecialCharacters={false}>
{helmetTitle && <title>{helmetTitle}</title>}
{helmetTitle && <meta property="og:title" content={helmetTitle} />}
{metaDesc && <meta name="description" content={metaDesc} />}

View File

@ -111,7 +111,7 @@ class ComponentDoc extends React.Component {
return (
<article className={articleClassName}>
<Helmet>
<Helmet encodeSpecialCharacters={false}>
{helmetTitle && <title>{helmetTitle}</title>}
{helmetTitle && <meta property="og:title" content={helmetTitle} />}
{contentChild && <meta name="description" content={contentChild} />}

View File

@ -41,19 +41,17 @@ function getStyle() {
#header .ant-row > div:last-child .header-lang-button {
margin-right: 0;
}
footer .footer-wrap {
width: 100%;
.rc-footer-container {
max-width: 1200px;
padding: 86px 24px 93px 24px;
margin: auto;
padding: 80px 0;
}
@media only screen and (max-width: 767.99px) {
#footer .footer-wrap {
padding: 40px 24px;
}
footer .footer-wrap .ant-row {
padding: 0;
}
.rc-footer-bottom-container {
max-width: 1200px;
}
.rc-footer-columns {
justify-content: space-around;
}
`;
}
@ -71,7 +69,7 @@ class Home extends React.Component {
return (
<>
<style dangerouslySetInnerHTML={{ __html: getStyle() }} /> {/* eslint-disable-line */}
<Helmet>
<Helmet encodeSpecialCharacters={false}>
<title>{`Ant Design - ${intl.formatMessage({ id: 'app.home.slogan' })}`}</title>
</Helmet>
<Banner {...childProps} />

View File

@ -1,20 +1,18 @@
import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Modal, message, Row, Col, Icon } from 'antd';
import { Modal, message, Icon } from 'antd';
import { Link } from 'bisheng/router';
import RcFooter from 'rc-footer';
import { presetPalettes } from '@ant-design/colors';
import { isLocalStorageNameSupported, loadScript, getLocalizedPathname } from '../utils';
import ColorPicker from '../Color/ColorPicker';
class Footer extends React.Component {
constructor(props) {
super(props);
lessLoaded = false;
this.lessLoaded = false;
this.state = {
color: '#1890ff',
};
}
state = {
color: presetPalettes.blue.primary,
};
componentDidMount() {
// for some iOS
@ -33,23 +31,303 @@ class Footer extends React.Component {
}
}
getColumns() {
const { intl = {} } = this.props;
const isZhCN = intl.locale === 'zh-CN';
return [
{
title: <FormattedMessage id="app.footer.resources" />,
items: [
{
title: 'Ant Design Pro',
url: 'https://pro.ant.design',
openExternal: true,
},
{
title: 'Ant Design Mobile',
url: 'https://mobile.ant.design',
openExternal: true,
},
{
title: 'NG-ZORRO',
description: 'Ant Design of Angular',
url: 'https://ng.ant.design',
openExternal: true,
},
{
title: 'NG-ZORRO-MOBILE',
url: 'https://ng.mobile.ant.design',
openExternal: true,
},
{
title: 'Ant Design Vue',
url: 'https://vue.ant.design',
openExternal: true,
},
{
title: 'Ant Design Landing',
description: <FormattedMessage id="app.footer.landing" />,
url: 'https://landing.ant.design',
openExternal: true,
},
{
title: 'Scaffolds',
description: <FormattedMessage id="app.footer.scaffolds" />,
url: 'https://scaffolds.ant.design',
openExternal: true,
},
{
title: 'Umi',
description: <FormattedMessage id="app.footer.umi" />,
url: 'https://umijs.org',
openExternal: true,
},
{
title: 'Dva',
description: <FormattedMessage id="app.footer.dva" />,
url: 'https://dvajs.com',
openExternal: true,
},
{
title: 'Ant Motion',
description: <FormattedMessage id="app.footer.motion" />,
url: 'https://motion.ant.design',
openExternal: true,
},
{
title: <FormattedMessage id="app.footer.design-resources" />,
url: getLocalizedPathname('/docs/spec/download', isZhCN),
LinkComponent: Link,
},
{
title: <FormattedMessage id="app.footer.chinamirror" />,
url: 'https://ant-design.gitee.io/',
},
],
},
{
title: <FormattedMessage id="app.footer.community" />,
items: [
{
icon: <Icon type="ant-design" />,
title: <FormattedMessage id="app.footer.awesome" />,
url: 'https://github.com/websemantics/awesome-ant-design',
openExternal: true,
},
{
icon: <Icon type="medium" />,
title: 'Medium',
url: 'http://medium.com/ant-design/',
openExternal: true,
},
{
icon: <Icon type="twitter" style={{ color: '#1DA1F2' }} />,
title: 'Twitter',
url: 'http://twitter.com/antdesignui',
openExternal: true,
},
{
icon: <Icon type="zhihu" style={{ color: '#0084ff' }} />,
title: <FormattedMessage id="app.footer.zhihu" />,
url: 'http://zhuanlan.zhihu.com/antdesign',
openExternal: true,
},
{
icon: <Icon type="zhihu" style={{ color: '#0084ff' }} />,
title: <FormattedMessage id="app.footer.zhihu.xtech" />,
url: 'http://zhuanlan.zhihu.com/xtech',
openExternal: true,
},
{
icon: <Icon type="zhihu" style={{ color: '#0084ff' }} />,
title: 'SEE Conf',
description: <FormattedMessage id="app.footer.seeconf" />,
url: 'http://zhuanlan.zhihu.com/xtech',
openExternal: true,
},
{
icon: <Icon type="usergroup-add" />,
title: <FormattedMessage id="app.footer.work_with_us" />,
url: getLocalizedPathname('/docs/spec/work-with-us', isZhCN),
LinkComponent: Link,
},
],
},
{
title: <FormattedMessage id="app.footer.help" />,
items: [
{
icon: <Icon type="github" />,
title: 'GitHub',
url: 'https://github.com/ant-design/ant-design',
openExternal: true,
},
{
icon: <Icon type="history" />,
title: <FormattedMessage id="app.footer.change-log" />,
url: getLocalizedPathname('/changelog', isZhCN),
LinkComponent: Link,
},
{
icon: <Icon type="profile" />,
title: <FormattedMessage id="app.footer.faq" />,
url: getLocalizedPathname('/docs/react/faq', isZhCN),
LinkComponent: Link,
},
{
icon: <Icon type="bug" />,
title: <FormattedMessage id="app.footer.bug-report" />,
url: 'https://new-issue.ant.design/',
openExternal: true,
},
{
icon: <Icon type="issues-close" />,
title: <FormattedMessage id="app.footer.issues" />,
url: 'https://github.com/ant-design/ant-design/issues',
openExternal: true,
},
{
icon: <Icon type="book" />,
title: <FormattedMessage id="app.footer.course" />,
url: 'https://www.yuque.com/ant-design/course',
openExternal: true,
},
{
icon: <Icon type="message" />,
title: <FormattedMessage id="app.footer.discuss-cn" />,
url: 'https://gitter.im/ant-design/ant-design',
openExternal: true,
},
{
icon: <Icon type="message" />,
title: <FormattedMessage id="app.footer.discuss-en" />,
url: 'https://gitter.im/ant-design/ant-design-english',
openExternal: true,
},
{
icon: <Icon type="question-circle" />,
title: <FormattedMessage id="app.footer.stackoverflow" />,
url: 'http://stackoverflow.com/questions/tagged/antd',
openExternal: true,
},
{
icon: <Icon type="question-circle" />,
title: <FormattedMessage id="app.footer.segmentfault" />,
url: 'https://segmentfault.com/t/antd',
openExternal: true,
},
],
},
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/rmsportal/nBVXkrFdWHxbZlmMbsaH.svg"
alt="AFX Cloud"
/>
),
title: <FormattedMessage id="app.footer.more-product" />,
items: [
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg"
alt="yuque"
/>
),
title: '语雀',
url: 'https://yuque.com',
description: '知识创作与分享工具',
openExternal: true,
},
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/rmsportal/uHocHZfNWZOdsRUonZNr.png"
alt="yunfengdie"
/>
),
title: '云凤蝶',
url: 'https://yunfengdie.com',
description: '中台建站平台',
openExternal: true,
},
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/antfincdn/nc7Fc0XBg5/8a6844f5-a6ed-4630-9177-4fa5d0b7dd47.png"
alt="AntV"
/>
),
title: 'AntV',
url: 'https://antv.alipay.com',
description: '数据可视化',
openExternal: true,
},
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/antfincdn/v2%24rh7lqpu/82f338dd-b0a6-41bc-9a86-58aaa9df217b.png"
alt="Egg"
/>
),
title: 'Egg',
url: 'https://eggjs.org',
description: '企业级 Node 开发框架',
openExternal: true,
},
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/rmsportal/DMDOlAUhmktLyEODCMBR.ico"
alt="kitchen"
/>
),
title: 'Kitchen',
description: <FormattedMessage id="app.footer.kitchen" />,
url: 'https://kitchen.alipay.com',
openExternal: true,
},
{
icon: (
<img
src="https://gw.alipayobjects.com/zos/rmsportal/nBVXkrFdWHxbZlmMbsaH.svg"
alt="xtech"
/>
),
title: '蚂蚁体验科技',
url: 'https://xtech.antfin.com/',
openExternal: true,
},
{
title: this.renderThemeChanger(),
style: {
marginTop: 20,
},
},
],
},
];
}
handleColorChange = color => {
const changeColor = () => {
const {
intl: { messages },
} = this.props;
const hide = message.loading(messages['app.footer.primary-color-changing']);
window.less
.modifyVars({
'@primary-color': color,
})
.then(() => {
hide();
Icon.setTwoToneColor({ primaryColor: color });
message.success(messages['app.footer.primary-color-changed']);
this.setState({ color });
});
};
const lessUrl = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js';
const lessUrl = 'https://gw.alipayobjects.com/os/lib/less/3.10.3/dist/less.min.js';
if (this.lessLoaded) {
changeColor();
@ -98,311 +376,38 @@ class Footer extends React.Component {
});
}
render() {
const { intl = {} } = this.props;
renderThemeChanger() {
const { color } = this.state;
const isZhCN = intl.locale === 'zh-CN';
const colors = Object.keys(presetPalettes).filter(item => item !== 'grey');
return (
<footer id="footer">
<div className="footer-wrap">
<Row gutter={16}>
<Col md={6} sm={24} xs={24}>
<div className="footer-center">
<h2>
<FormattedMessage id="app.footer.resources" />
</h2>
<div>
<a href="http://pro.ant.design">Ant Design Pro</a>
</div>
<div>
<a href="http://mobile.ant.design">Ant Design Mobile</a>
</div>
<div>
<a href="http://ng.ant.design">NG-ZORRO</a>
<span> - </span>
<span>Ant Design of Angular</span>
</div>
<div>
<a href="http://ng.mobile.ant.design">NG-ZORRO-MOBILE</a>
</div>
<div>
<a href="http://vue.ant.design">Ant Design Vue</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://kitchen.alipay.com">
Kitchen
</a>
<span> - </span>
<FormattedMessage id="app.footer.kitchen" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://landing.ant.design">
Ant Design Landing
</a>
<span> - </span>
<FormattedMessage id="app.footer.landing" />
</div>
<div>
<a href="http://scaffold.ant.design">Scaffolds</a>
<span> - </span>
<FormattedMessage id="app.footer.scaffolds" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://umijs.org/">
Umi
</a>{' '}
- <FormattedMessage id="app.footer.umi" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://github.com/dvajs/dva">
dva
</a>{' '}
- <FormattedMessage id="app.footer.dva" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://motion.ant.design">
Ant Motion
</a>
<span> - </span>
<FormattedMessage id="app.footer.motion" />
</div>
<div>
<Link to={getLocalizedPathname('/docs/spec/download', isZhCN)}>
<FormattedMessage id="app.footer.design-resources" />
</Link>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://ant-design.gitee.io/">
<FormattedMessage id="app.footer.chinamirror" />
</a>
</div>
</div>
</Col>
<Col md={6} sm={24} xs={24}>
<div className="footer-center">
<h2>
<FormattedMessage id="app.footer.community" />
</h2>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/websemantics/awesome-ant-design"
>
<Icon type="ant-design" /> <FormattedMessage id="app.footer.awesome" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://medium.com/ant-design/">
<Icon type="medium" /> Medium
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="http://twitter.com/antdesignui"
>
<Icon type="twitter" style={{ color: '#1DA1F2' }} /> Twitter
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="http://zhuanlan.zhihu.com/antdesign"
>
<Icon type="zhihu" style={{ color: '#0084ff' }} />{' '}
<FormattedMessage id="app.footer.zhihu" />
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="http://zhuanlan.zhihu.com/xtech"
>
<Icon type="zhihu" style={{ color: '#0084ff' }} />{' '}
<FormattedMessage id="app.footer.zhihu.xtech" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://seeconf.alipay.com/">
SEE Conf
</a>
<span> - </span>
<FormattedMessage id="app.footer.seeconf" />
</div>
<div>
<Link to={getLocalizedPathname('/docs/spec/work-with-us', isZhCN)}>
<FormattedMessage id="app.footer.work_with_us" />
</Link>
</div>
</div>
</Col>
<Col md={6} sm={24} xs={24}>
<div className="footer-center">
<h2>
<FormattedMessage id="app.footer.help" />
</h2>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/ant-design/ant-design"
>
GitHub
</a>
</div>
<div>
<Link to={getLocalizedPathname('/changelog', isZhCN)}>
<FormattedMessage id="app.footer.change-log" />
</Link>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://www.yuque.com/ant-design/course"
>
<FormattedMessage id="app.footer.course" />
</a>
</div>
<div>
<Link to={getLocalizedPathname('/docs/react/faq', isZhCN)}>
<FormattedMessage id="app.footer.faq" />
</Link>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://gitter.im/ant-design/ant-design"
>
<FormattedMessage id="app.footer.discuss-cn" />
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://gitter.im/ant-design/ant-design-english"
>
<FormattedMessage id="app.footer.discuss-en" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://new-issue.ant.design/">
<FormattedMessage id="app.footer.bug-report" />
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/ant-design/ant-design/issues"
>
<FormattedMessage id="app.footer.issues" />
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="http://stackoverflow.com/questions/tagged/antd"
>
<FormattedMessage id="app.footer.stackoverflow" />
</a>
</div>
<div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://segmentfault.com/t/antd"
>
<FormattedMessage id="app.footer.segmentfault" />
</a>
</div>
</div>
</Col>
<Col md={6} sm={24} xs={24}>
<div className="footer-center">
<h2>
<img
className="title-icon"
src="https://gw.alipayobjects.com/zos/rmsportal/nBVXkrFdWHxbZlmMbsaH.svg"
alt="AFX Cloud"
/>
<FormattedMessage id="app.footer.more-product" />
</h2>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://yuque.com/">
<Icon type="yuque" theme="filled" style={{ color: '#25b864' }} />{' '}
<FormattedMessage id="app.footer.yuque" />
</a>
<span> - </span>
<FormattedMessage id="app.footer.yuque.slogan" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://yunfengdie.com/">
<FormattedMessage id="app.footer.fengdie" />
</a>
<span> - </span>
<FormattedMessage id="app.footer.fengdie.slogan" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://antv.alipay.com/">
AntV
</a>
<span> - </span>
<FormattedMessage id="app.footer.data-vis" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://eggjs.org/">
Egg
</a>
<span> - </span>
<FormattedMessage id="app.footer.eggjs" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://xtech.antfin.com/">
<FormattedMessage id="app.footer.xtech" />
</a>
</div>
<div style={{ marginTop: 20 }}>
<ColorPicker
type="sketch"
small
color={color}
position="top"
presetColors={[
'#F5222D',
'#FA541C',
'#FA8C16',
'#FAAD14',
'#FADB14',
'#A0D911',
'#52C41A',
'#13C2C2',
'#1890FF',
'#2F54EB',
'#722ED1',
'#EB2F96',
]}
onChangeComplete={this.handleColorChange}
/>
</div>
</div>
</Col>
</Row>
</div>
<div className="bottom-bar">
Made with <span className="heart"></span> by
<a target="_blank" rel="noopener noreferrer" href="https://xtech.antfin.com">
<FormattedMessage id="app.footer.company" />
</a>
</div>
</footer>
<ColorPicker
type="sketch"
small
color={color}
position="top"
presetColors={[
...colors.map(c => presetPalettes[c][5]),
...colors.map(c => presetPalettes[c][4]),
...colors.map(c => presetPalettes[c][6]),
]}
onChangeComplete={this.handleColorChange}
/>
);
}
render() {
return (
<RcFooter
columns={this.getColumns()}
bottom={
<>
Made with <span style={{ color: '#fff' }}></span> by{' '}
<a target="_blank" rel="noopener noreferrer" href="https://xtech.antfin.com">
<FormattedMessage id="app.footer.company" />
</a>
</>
}
/>
);
}
}

View File

@ -120,7 +120,7 @@ export default class Layout extends React.Component {
: 'An enterprise-class UI design language and React UI library with a set of high-quality React components, one of best React UI library for enterprises';
return (
<>
<Helmet>
<Helmet encodeSpecialCharacters={false}>
<html lang={appLocale.locale === 'zh-CN' ? 'zh' : 'en'} />
<title>{title}</title>
<meta name="description" content={description} />

View File

@ -51,6 +51,7 @@ module.exports = {
'app.footer.awesome': 'Awesome Ant Design',
'app.footer.course': 'Ant Design 实战教程',
'app.footer.chinamirror': '国内镜像站点 🇨🇳',
'app.footer.primary-color-changing': '正在修改主题色...',
'app.footer.primary-color-changed': '修改主题色成功!',
'app.footer.kitchen': 'Sketch 工具集',
'app.footer.landing': '首页模板集',
@ -73,8 +74,8 @@ module.exports = {
'app.footer.feedback': '反馈和建议',
'app.footer.stackoverflow': 'StackOverflow',
'app.footer.segmentfault': 'SegmentFault',
'app.footer.discuss-en': '在线讨论 (English)',
'app.footer.discuss-cn': '在线讨论 (中文)',
'app.footer.discuss-en': '在线讨论 🇺🇸',
'app.footer.discuss-cn': '在线讨论 🇨🇳',
'app.footer.bug-report': '报告 Bug',
'app.footer.issues': '讨论列表',
'app.footer.version': '文档版本:',