mirror of
https://github.com/ant-design/ant-design.git
synced 2025-08-06 16:06:28 +08:00
Merge branch 'antd-3.0'
This commit is contained in:
commit
7fa0599695
5
.jest.js
5
.jest.js
@ -42,4 +42,9 @@ module.exports = {
|
||||
snapshotSerializers: [
|
||||
'enzyme-to-json/serializer',
|
||||
],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -18,4 +18,9 @@ module.exports = {
|
||||
snapshotSerializers: [
|
||||
'enzyme-to-json/serializer'
|
||||
],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -16,6 +16,8 @@
|
||||
"selector-list-comma-newline-after": null,
|
||||
"selector-pseudo-element-colon-notation": null,
|
||||
"unit-no-unknown": null,
|
||||
"value-list-max-empty-lines": null
|
||||
"value-list-max-empty-lines": null,
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"no-descending-specificity": null
|
||||
}
|
||||
}
|
||||
|
47
.travis.yml
47
.travis.yml
@ -5,33 +5,24 @@ language: node_js
|
||||
node_js:
|
||||
- 8
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- TEST_TYPE=lint
|
||||
- TEST_TYPE=test:dist
|
||||
- TEST_TYPE=test:lib
|
||||
- TEST_TYPE=test:es
|
||||
- TEST_TYPE=test:dom
|
||||
- TEST_TYPE=test:node
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- env: TEST_TYPE=lint
|
||||
- env: REACT=16 TEST_TYPE=test:dist
|
||||
- env: REACT=16 TEST_TYPE=test:lib
|
||||
- env: REACT=16 TEST_TYPE=test:es
|
||||
- env: REACT=16 TEST_TYPE=test:dom
|
||||
- env: REACT=16 TEST_TYPE=test:node
|
||||
- env: REACT=15 TEST_TYPE=test:dist
|
||||
- env: REACT=15 TEST_TYPE=test:lib
|
||||
- env: REACT=15 TEST_TYPE=test:es
|
||||
- env: REACT=15 TEST_TYPE=test:dom
|
||||
- env: REACT=15 TEST_TYPE=test:node
|
||||
- env: REACT=16 TEST_TYPE=bisheng:build
|
||||
|
||||
before_script:
|
||||
- scripts/install-react.sh
|
||||
|
||||
script:
|
||||
- |
|
||||
if [ "$TEST_TYPE" = lint ]; then
|
||||
npm run lint
|
||||
elif [ "$TEST_TYPE" = test:dist ]; then
|
||||
npm run dist && \
|
||||
node ./tests/dekko/dist.test.js && \
|
||||
LIB_DIR=dist npm test -- -w 2
|
||||
elif [ "$TEST_TYPE" = test:lib ]; then
|
||||
npm run compile && \
|
||||
node ./tests/dekko/lib.test.js && \
|
||||
LIB_DIR=lib npm test -- -w 2
|
||||
elif [ "$TEST_TYPE" = test:es ]; then
|
||||
npm run compile && \
|
||||
LIB_DIR=es npm test -- -w 2
|
||||
elif [ "$TEST_TYPE" = test:dom ]; then
|
||||
npm test -- --coverage -w 2 && \
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
elif [ "$TEST_TYPE" = test:node ]; then
|
||||
npm run test-node -- -w 2
|
||||
fi
|
||||
- scripts/travis-script.sh
|
||||
|
@ -15,7 +15,58 @@ timeline: true
|
||||
|
||||
如果需要查看 `2.0.0` 之前的更新日志,请移步 [GitHub](https://github.com/ant-design/ant-design/blob/1.x-stable/CHANGELOG.md)。
|
||||
|
||||
---
|
||||
---## 3.0.0
|
||||
|
||||
- Select
|
||||
- 单选和多选模式 Option 支持 number。
|
||||
- 新增 `maxTagCount` 和 `maxTagPlaceholder`。
|
||||
- 新增 `showAction`。
|
||||
- 新增 `onMouseEnter` 和 `onMouseLeave`。
|
||||
- 新增 `focus()`、`blur()` 和 `autoFocus`。
|
||||
- Table
|
||||
- 新增 `components` 属性,可以通过该属性覆盖 table 默认元素:
|
||||
```javascript
|
||||
const components = {
|
||||
table: MyTable,
|
||||
header: {
|
||||
wrapper: HeaderWrapper,
|
||||
row: HeaderRow,
|
||||
cell: HeaderCell,
|
||||
},
|
||||
body: {
|
||||
wrapper: BodyWrapper,
|
||||
row: BodyRow,
|
||||
cell: BodyCell,
|
||||
},
|
||||
};
|
||||
|
||||
<Table components={components} columns={columns data={data}} />
|
||||
```
|
||||
- 新增 `onRow` 用于设置行属性。
|
||||
- 新增 `onHeaderRow` 用于设置头部的行属性。
|
||||
- 新增 `column[onCell]` 用于设置单元格属性 。
|
||||
- 新增 `column[onHeaderCell]` 用于设置头部单元格属性。
|
||||
- 新增 `column[align]` 用于设置列文字对其。
|
||||
- 废弃以下属性
|
||||
- `onRowClick`
|
||||
- `onRowDoubleClick`
|
||||
- `onRowContextMenu`
|
||||
- 'onRowMouseEnter'
|
||||
- 'onRowMouseLeave'
|
||||
|
||||
以上属性请使用 `onRow` 代替:
|
||||
```javascript
|
||||
<Table onRow={(record) => ({
|
||||
onClick: () => {},
|
||||
onDoubleClick: () => {},
|
||||
onContextMenu: () => {},
|
||||
onMouseEnter: () => {},
|
||||
onMouseLeave: () => {},
|
||||
})} />
|
||||
```
|
||||
- 废弃 `getBodyWrapper`, 请使用 `components` 代替。
|
||||
|
||||
|
||||
|
||||
## 2.13.10
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="http://ant.design">
|
||||
<img width="320" src="https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg">
|
||||
<img width="230" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -60,24 +60,11 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
|
||||
## TypeScript
|
||||
|
||||
```js
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> - 设置 `allowSyntheticDefaultImports` 避免 `error TS1192: Module 'react' has no default export` 的错误。
|
||||
> - 不要使用 @types/antd, antd 已经自带了 TypeScript 定义。
|
||||
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)
|
||||
|
||||
## 国际化
|
||||
|
||||
参考 [国际化文档](http://ant.design/docs/react/i18n)。
|
||||
参考 [国际化文档](http://ant.design/docs/react/i18n-cn)。
|
||||
|
||||
## 链接
|
||||
|
||||
@ -93,7 +80,7 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
- [开发者说明](https://github.com/ant-design/ant-design/wiki/Development)
|
||||
- [版本发布规则](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
|
||||
- [常见问题](https://github.com/ant-design/ant-design/wiki/FAQ)
|
||||
- [CodePen 模板](http://codepen.io/benjycui/pen/KgPZrE?editors=001) for bug reports
|
||||
- [CodeSandbox 模板](https://u.ant.design/codesandbox-repro) for bug reports
|
||||
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
|
||||
- [定制主题](http://ant.design/docs/react/customize-theme-cn)
|
||||
|
||||
|
31
README.md
31
README.md
@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="http://ant.design">
|
||||
<img width="320" src="https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg">
|
||||
<img width="230" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -86,33 +86,8 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
|
||||
|
||||
### TypeScript
|
||||
|
||||
```js
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
||||
```
|
||||
See [Use in TypeScript](https://ant.design/docs/react/use-in-typescript)
|
||||
|
||||
> Note:
|
||||
> - set `allowSyntheticDefaultImports` to prevent `error TS1192: Module 'react' has no default export`.
|
||||
> - Don't use @types/antd, antd provide a built-in ts definition already.
|
||||
|
||||
#### Use [ts-import-plugin](https://github.com/Brooooooklyn/ts-import-plugin) with modularized antd
|
||||
|
||||
```js
|
||||
{
|
||||
loader: "ts-loader", // or awesome-typescript-loader
|
||||
options {
|
||||
getCustomTransformers: () => ({
|
||||
before: [ tsImportPluginFactory({ libraryName: "antd", style: "css" }) ]
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Internationalization
|
||||
|
||||
@ -131,7 +106,7 @@ See [i18n](http://ant.design/docs/react/i18n).
|
||||
- [Developer Instruction](https://github.com/ant-design/ant-design/wiki/Development)
|
||||
- [Versioning Release Note](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
|
||||
- [FAQ](https://github.com/ant-design/ant-design/wiki/FAQ)
|
||||
- [CodePen boilerplate](http://codepen.io/benjycui/pen/KgPZrE?editors=001) for bug reports
|
||||
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) for bug reports
|
||||
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
|
||||
- [Customize Theme](http://ant.design/docs/react/customize-theme)
|
||||
|
||||
|
@ -19,12 +19,14 @@ Array [
|
||||
"Checkbox",
|
||||
"Col",
|
||||
"DatePicker",
|
||||
"Divider",
|
||||
"Dropdown",
|
||||
"Form",
|
||||
"Icon",
|
||||
"Input",
|
||||
"InputNumber",
|
||||
"Layout",
|
||||
"List",
|
||||
"LocaleProvider",
|
||||
"message",
|
||||
"Menu",
|
||||
|
4
components/_util/callMoment.tsx
Normal file
4
components/_util/callMoment.tsx
Normal file
@ -0,0 +1,4 @@
|
||||
// https://github.com/moment/moment/issues/3650
|
||||
export default function callMoment(moment: any, ...args: any[]) {
|
||||
return (moment.default || moment)(...args);
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
export function getComponentLocale(props, context, componentName, getDefaultLocale) {
|
||||
let locale: any = {};
|
||||
if (context && context.antLocale && context.antLocale[componentName]) {
|
||||
locale = context.antLocale[componentName];
|
||||
} else {
|
||||
const defaultLocale = getDefaultLocale();
|
||||
// TODO: make default lang of antd be English
|
||||
// https://github.com/ant-design/ant-design/issues/6334
|
||||
locale = defaultLocale.default || defaultLocale;
|
||||
}
|
||||
|
||||
const result = {
|
||||
...locale,
|
||||
...props.locale,
|
||||
};
|
||||
result.lang = {
|
||||
...locale.lang,
|
||||
...props.locale.lang,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getLocaleCode(context) {
|
||||
const localeCode = context.antLocale && context.antLocale.locale;
|
||||
// Had use LocaleProvide but didn't set locale
|
||||
if (context.antLocale && context.antLocale.exist && !localeCode) {
|
||||
return 'zh-cn';
|
||||
}
|
||||
return localeCode;
|
||||
}
|
@ -2,7 +2,7 @@ const availablePrefixs = ['moz', 'ms', 'webkit'];
|
||||
|
||||
function requestAnimationFramePolyfill() {
|
||||
let lastTime = 0;
|
||||
return function(callback) {
|
||||
return function(callback: (n: number) => void) {
|
||||
const currTime = new Date().getTime();
|
||||
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||
const id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
|
||||
@ -23,11 +23,11 @@ export default function getRequestAnimationFrame() {
|
||||
const prefix = availablePrefixs.filter(key => `${key}RequestAnimationFrame` in window)[0];
|
||||
|
||||
return prefix
|
||||
? window[`${prefix}RequestAnimationFrame`]
|
||||
? (window as any)[`${prefix}RequestAnimationFrame`]
|
||||
: requestAnimationFramePolyfill();
|
||||
}
|
||||
|
||||
export function cancelRequestAnimationFrame(id) {
|
||||
export function cancelRequestAnimationFrame(id: number) {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
@ -39,6 +39,8 @@ export function cancelRequestAnimationFrame(id) {
|
||||
)[0];
|
||||
|
||||
return prefix ?
|
||||
(window[`${prefix}CancelAnimationFrame`] || window[`${prefix}CancelRequestAnimationFrame`]).call(this, id)
|
||||
: clearTimeout(id);
|
||||
(
|
||||
(window as any)[`${prefix}CancelAnimationFrame`] ||
|
||||
(window as any)[`${prefix}CancelRequestAnimationFrame`]
|
||||
).call(this, id) : clearTimeout(id);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export default function getScroll(target, top): number {
|
||||
export default function getScroll(target: any, top: boolean): number {
|
||||
if (typeof window === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
let animation;
|
||||
let animation: boolean;
|
||||
|
||||
function isCssAnimationSupported() {
|
||||
if (animation !== undefined) {
|
||||
@ -11,7 +11,7 @@ function isCssAnimationSupported() {
|
||||
}
|
||||
if (animation !== undefined) {
|
||||
for (let i = 0; i < domPrefixes.length; i++) {
|
||||
if (elm.style[`${domPrefixes[i]}AnimationName`] !== undefined) {
|
||||
if ((elm.style as any)[`${domPrefixes[i]}AnimationName`] !== undefined) {
|
||||
animation = true;
|
||||
break;
|
||||
}
|
||||
|
@ -3,18 +3,18 @@ import getRequestAnimationFrame, { cancelRequestAnimationFrame } from './getRequ
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
|
||||
function animate(node, show, done) {
|
||||
let height;
|
||||
let requestAnimationFrameId;
|
||||
function animate(node: HTMLElement, show: boolean, done: () => void) {
|
||||
let height: number;
|
||||
let requestAnimationFrameId: number;
|
||||
return cssAnimation(node, 'ant-motion-collapse', {
|
||||
start() {
|
||||
if (!show) {
|
||||
node.style.height = `${node.offsetHeight}px`;
|
||||
node.style.opacity = 1;
|
||||
node.style.opacity = '1';
|
||||
} else {
|
||||
height = node.offsetHeight;
|
||||
node.style.height = 0;
|
||||
node.style.opacity = 0;
|
||||
node.style.height = '0 px';
|
||||
node.style.opacity = '0';
|
||||
}
|
||||
},
|
||||
active() {
|
||||
@ -23,7 +23,7 @@ function animate(node, show, done) {
|
||||
}
|
||||
requestAnimationFrameId = reqAnimFrame(() => {
|
||||
node.style.height = `${show ? height : 0}px`;
|
||||
node.style.opacity = show ? 1 : 0;
|
||||
node.style.opacity = show ? '1' : '0';
|
||||
});
|
||||
},
|
||||
end() {
|
||||
@ -38,13 +38,13 @@ function animate(node, show, done) {
|
||||
}
|
||||
|
||||
const animation = {
|
||||
enter(node, done) {
|
||||
enter(node: HTMLElement, done: () => void) {
|
||||
return animate(node, true, done);
|
||||
},
|
||||
leave(node, done) {
|
||||
leave(node: HTMLElement, done: () => void) {
|
||||
return animate(node, false, done);
|
||||
},
|
||||
appear(node, done) {
|
||||
appear(node: HTMLElement, done: () => void) {
|
||||
return animate(node, true, done);
|
||||
},
|
||||
};
|
||||
|
@ -2,27 +2,27 @@ import getRequestAnimationFrame, { cancelRequestAnimationFrame } from '../_util/
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
|
||||
export default function throttleByAnimationFrame(fn) {
|
||||
let requestId;
|
||||
export default function throttleByAnimationFrame(fn: () => void) {
|
||||
let requestId: number | null;
|
||||
|
||||
const later = args => () => {
|
||||
const later = (args: any[]) => () => {
|
||||
requestId = null;
|
||||
fn(...args);
|
||||
};
|
||||
|
||||
const throttled = (...args) => {
|
||||
const throttled = (...args: any[]) => {
|
||||
if (requestId == null) {
|
||||
requestId = reqAnimFrame(later(args));
|
||||
}
|
||||
};
|
||||
|
||||
(throttled as any).cancel = () => cancelRequestAnimationFrame(requestId);
|
||||
(throttled as any).cancel = () => cancelRequestAnimationFrame(requestId!);
|
||||
|
||||
return throttled;
|
||||
}
|
||||
|
||||
export function throttleByAnimationFrameDecorator() {
|
||||
return function(target, key, descriptor) {
|
||||
return function(target: any, key: string, descriptor: any) {
|
||||
let fn = descriptor.value;
|
||||
let definingProperty = false;
|
||||
return {
|
||||
|
@ -1,4 +1,4 @@
|
||||
export default function triggerEvent(el, type) {
|
||||
export default function triggerEvent(el: Element, type: string) {
|
||||
if ('createEvent' in document) {
|
||||
// modern browsers, IE9+
|
||||
const e = document.createEvent('HTMLEvents');
|
||||
|
@ -60,7 +60,7 @@ describe('Affix Render', () => {
|
||||
const wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
jest.runAllTimers();
|
||||
|
||||
wrapper.node.affix.refs.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => {
|
||||
wrapper.instance().affix.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => {
|
||||
return {
|
||||
bottom: 100, height: 28, left: 0, right: 0, top: -50, width: 195,
|
||||
};
|
||||
@ -71,6 +71,6 @@ describe('Affix Render', () => {
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.node.affix.state.affixStyle).not.toBe(null);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import classNames from 'classnames';
|
||||
@ -8,13 +8,13 @@ import omit from 'omit.js';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
|
||||
|
||||
function getTargetRect(target): ClientRect {
|
||||
function getTargetRect(target: HTMLElement | Window | null): ClientRect {
|
||||
return target !== window ?
|
||||
target.getBoundingClientRect() :
|
||||
{ top: 0, left: 0, bottom: 0 };
|
||||
(target as HTMLElement).getBoundingClientRect() :
|
||||
{ top: 0, left: 0, bottom: 0 } as ClientRect;
|
||||
}
|
||||
|
||||
function getOffset(element: HTMLElement, target) {
|
||||
function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
|
||||
const elemRect = element.getBoundingClientRect();
|
||||
const targetRect = getTargetRect(target);
|
||||
|
||||
@ -55,11 +55,16 @@ export interface AffixProps {
|
||||
/** 固定状态改变时触发的回调函数 */
|
||||
onChange?: (affixed?: boolean) => void;
|
||||
/** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
|
||||
target?: () => Window | HTMLElement;
|
||||
target?: () => Window | HTMLElement | null;
|
||||
prefixCls?: string;
|
||||
}
|
||||
|
||||
export default class Affix extends React.Component<AffixProps, any> {
|
||||
export interface AffixState {
|
||||
affixStyle: React.CSSProperties | undefined;
|
||||
placeholderStyle: React.CSSProperties | undefined;
|
||||
}
|
||||
|
||||
export default class Affix extends React.Component<AffixProps, AffixState> {
|
||||
static propTypes = {
|
||||
offsetTop: PropTypes.number,
|
||||
offsetBottom: PropTypes.number,
|
||||
@ -69,9 +74,6 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
scrollEvent: any;
|
||||
resizeEvent: any;
|
||||
timeout: any;
|
||||
refs: {
|
||||
fixedNode: HTMLElement;
|
||||
};
|
||||
|
||||
events = [
|
||||
'resize',
|
||||
@ -83,17 +85,18 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
'load',
|
||||
];
|
||||
|
||||
eventHandlers = {};
|
||||
eventHandlers: {
|
||||
[key: string]: any;
|
||||
} = {};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
affixStyle: null,
|
||||
placeholderStyle: null,
|
||||
};
|
||||
}
|
||||
state: AffixState = {
|
||||
affixStyle: undefined,
|
||||
placeholderStyle: undefined,
|
||||
};
|
||||
|
||||
setAffixStyle(e, affixStyle) {
|
||||
private fixedNode: HTMLElement;
|
||||
|
||||
setAffixStyle(e: any, affixStyle: React.CSSProperties | null) {
|
||||
const { onChange = noop, target = getDefaultTarget } = this.props;
|
||||
const originalAffixStyle = this.state.affixStyle;
|
||||
const isWindow = target() === window;
|
||||
@ -103,7 +106,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
if (shallowequal(affixStyle, originalAffixStyle)) {
|
||||
return;
|
||||
}
|
||||
this.setState({ affixStyle }, () => {
|
||||
this.setState({ affixStyle: affixStyle as React.CSSProperties }, () => {
|
||||
const affixed = !!this.state.affixStyle;
|
||||
if ((affixStyle && !originalAffixStyle) ||
|
||||
(!affixStyle && originalAffixStyle)) {
|
||||
@ -112,16 +115,16 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
});
|
||||
}
|
||||
|
||||
setPlaceholderStyle(placeholderStyle) {
|
||||
setPlaceholderStyle(placeholderStyle: React.CSSProperties | null) {
|
||||
const originalPlaceholderStyle = this.state.placeholderStyle;
|
||||
if (shallowequal(placeholderStyle, originalPlaceholderStyle)) {
|
||||
return;
|
||||
}
|
||||
this.setState({ placeholderStyle });
|
||||
this.setState({ placeholderStyle: placeholderStyle as React.CSSProperties });
|
||||
}
|
||||
|
||||
@throttleByAnimationFrameDecorator()
|
||||
updatePosition(e) {
|
||||
updatePosition(e: any) {
|
||||
let { offsetTop, offsetBottom, offset, target = getDefaultTarget } = this.props;
|
||||
const targetNode = target();
|
||||
|
||||
@ -131,8 +134,8 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
const affixNode = ReactDOM.findDOMNode(this) as HTMLElement;
|
||||
const elemOffset = getOffset(affixNode, targetNode);
|
||||
const elemSize = {
|
||||
width: this.refs.fixedNode.offsetWidth,
|
||||
height: this.refs.fixedNode.offsetHeight,
|
||||
width: this.fixedNode.offsetWidth,
|
||||
height: this.fixedNode.offsetHeight,
|
||||
};
|
||||
|
||||
const offsetMode = {
|
||||
@ -200,10 +203,10 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps: AffixProps) {
|
||||
if (this.props.target !== nextProps.target) {
|
||||
this.clearEventListeners();
|
||||
this.setTargetEventListeners(nextProps.target);
|
||||
this.setTargetEventListeners(nextProps.target!);
|
||||
|
||||
// Mock Event object.
|
||||
this.updatePosition({});
|
||||
@ -216,7 +219,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
(this.updatePosition as any).cancel();
|
||||
}
|
||||
|
||||
setTargetEventListeners(getTarget) {
|
||||
setTargetEventListeners(getTarget: () => HTMLElement | Window | null) {
|
||||
const target = getTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
@ -237,6 +240,10 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
});
|
||||
}
|
||||
|
||||
saveFixedNode = (node: HTMLDivElement) => {
|
||||
this.fixedNode = node;
|
||||
}
|
||||
|
||||
render() {
|
||||
const className = classNames({
|
||||
[this.props.prefixCls || 'ant-affix']: this.state.affixStyle,
|
||||
@ -246,7 +253,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
const placeholderStyle = { ...this.state.placeholderStyle, ...this.props.style };
|
||||
return (
|
||||
<div {...props} style={placeholderStyle}>
|
||||
<div className={className} ref="fixedNode" style={this.state.affixStyle}>
|
||||
<div className={className} ref={this.saveFixedNode} style={this.state.affixStyle}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import Animate from 'rc-animate';
|
||||
import Icon from '../icon';
|
||||
import classNames from 'classnames';
|
||||
|
@ -1,4 +1,5 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@alert-prefix-cls: ~"@{ant-prefix}-alert";
|
||||
|
||||
@ -6,75 +7,71 @@
|
||||
@alert-text-color: @text-color;
|
||||
|
||||
.@{alert-prefix-cls} {
|
||||
.reset-component;
|
||||
position: relative;
|
||||
padding: 8px 48px 8px 38px;
|
||||
padding: 8px 15px 8px 37px;
|
||||
border-radius: @border-radius-base;
|
||||
color: @alert-text-color;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
|
||||
&&-no-icon {
|
||||
padding: 8px 48px 8px 16px;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
font-size: @font-size-lg;
|
||||
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-lg / 2;
|
||||
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2 + 1px;
|
||||
left: 16px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&-description {
|
||||
font-size: @font-size-base;
|
||||
line-height: 21px;
|
||||
line-height: 22px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-success {
|
||||
border: @border-width-base @border-style-base @green-2;
|
||||
background-color: @green-1;
|
||||
border: @border-width-base @border-style-base ~`colorPalette("@{success-color}", 3)`;
|
||||
background-color: ~`colorPalette("@{success-color}", 1)`;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @success-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-info {
|
||||
border: @border-width-base @border-style-base @primary-2;
|
||||
background-color: @primary-1;
|
||||
border: @border-width-base @border-style-base ~`colorPalette("@{info-color}", 3)`;
|
||||
background-color: ~`colorPalette("@{info-color}", 1)`;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @info-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-warning {
|
||||
border: @border-width-base @border-style-base @yellow-2;
|
||||
background-color: @yellow-1;
|
||||
border: @border-width-base @border-style-base ~`colorPalette("@{warning-color}", 3)`;
|
||||
background-color: ~`colorPalette("@{warning-color}", 1)`;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @warning-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-error {
|
||||
border: @border-width-base @border-style-base @red-2;
|
||||
background-color: @red-1;
|
||||
border: @border-width-base @border-style-base ~`colorPalette("@{error-color}", 3)`;
|
||||
background-color: ~`colorPalette("@{error-color}", 1)`;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-close-icon {
|
||||
font-size: @font-size-base;
|
||||
font-size: @font-size-sm;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 10px;
|
||||
height: 12px;
|
||||
line-height: 12px;
|
||||
top: 8px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
.@{iconfont-css-prefix}-cross {
|
||||
color: @text-color-secondary;
|
||||
transition: color .3s ease;
|
||||
transition: color .3s;
|
||||
&:hover {
|
||||
color: #404040;
|
||||
}
|
||||
@ -87,21 +84,21 @@
|
||||
}
|
||||
|
||||
&-with-description {
|
||||
padding: 16px 16px 16px 60px;
|
||||
padding: 15px 15px 15px 64px;
|
||||
position: relative;
|
||||
border-radius: @border-radius-base;
|
||||
color: @text-color;
|
||||
line-height: 1.5;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
|
||||
&-with-description&-no-icon {
|
||||
padding: 16px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&-with-description &-icon {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 20px;
|
||||
left: 24px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
@ -42,7 +42,7 @@ function easeInOutCubic(t: number, b: number, c: number, d: number) {
|
||||
}
|
||||
|
||||
const reqAnimFrame = getRequestAnimationFrame();
|
||||
function scrollTo(href: string, offsetTop = 0, target, callback = () => { }) {
|
||||
function scrollTo(href: string, offsetTop = 0, target: () => Window | HTMLElement, callback = () => { }) {
|
||||
const scrollTop = getScroll(target(), true);
|
||||
const targetElement = document.getElementById(href.substring(1));
|
||||
if (!targetElement) {
|
||||
@ -95,9 +95,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
||||
antAnchor: PropTypes.object,
|
||||
};
|
||||
|
||||
refs: {
|
||||
ink?: any;
|
||||
};
|
||||
private inkNode: HTMLSpanElement;
|
||||
|
||||
private links: String[];
|
||||
private scrollEvent: any;
|
||||
@ -157,7 +155,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
||||
});
|
||||
}
|
||||
|
||||
handleScrollTo = (link) => {
|
||||
handleScrollTo = (link: string) => {
|
||||
const { offsetTop, target = getDefaultTarget } = this.props;
|
||||
this.animating = true;
|
||||
this.setState({ activeLink: link });
|
||||
@ -198,10 +196,14 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
||||
const { prefixCls } = this.props;
|
||||
const linkNode = ReactDOM.findDOMNode(this as any).getElementsByClassName(`${prefixCls}-link-title-active`)[0];
|
||||
if (linkNode) {
|
||||
this.refs.ink.style.top = `${(linkNode as any).offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
|
||||
this.inkNode.style.top = `${(linkNode as any).offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
|
||||
}
|
||||
}
|
||||
|
||||
saveInkNode = (node: HTMLSpanElement) => {
|
||||
this.inkNode = node;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls,
|
||||
@ -228,7 +230,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
|
||||
<div className={wrapperClass} style={style}>
|
||||
<div className={anchorClass}>
|
||||
<div className={`${prefixCls}-ink`} >
|
||||
<span className={inkClass} ref="ink" />
|
||||
<span className={inkClass} ref={this.saveInkNode} />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
@ -14,7 +14,7 @@ describe('Anchor Render', () => {
|
||||
|
||||
wrapper.find('a[href="#API"]').simulate('click');
|
||||
|
||||
wrapper.node.handleScroll();
|
||||
expect(wrapper.node.state).not.toBe(null);
|
||||
wrapper.instance().handleScroll();
|
||||
expect(wrapper.instance().state).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
@ -79,7 +79,7 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/fixed.md correctly 1`] = `
|
||||
exports[`renders ./components/anchor/demo/static.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
>
|
||||
|
@ -1,8 +1,8 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 固定
|
||||
en-US: Fixed Anchor
|
||||
zh-CN: 静态位置
|
||||
en-US: Static Anchor
|
||||
---
|
||||
|
||||
## zh-CN
|
@ -1,5 +1,8 @@
|
||||
import Anchor from './Anchor';
|
||||
import AnchorLink from './AnchorLink';
|
||||
|
||||
export { AnchorProps } from './Anchor';
|
||||
export { AnchorLinkProps } from './AnchorLink';
|
||||
|
||||
Anchor.Link = AnchorLink;
|
||||
export default Anchor;
|
||||
|
@ -1,7 +1,12 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@anchor-border-width: 2px;
|
||||
|
||||
.@{ant-prefix}-anchor {
|
||||
.reset-component;
|
||||
position: relative;
|
||||
padding-left: @anchor-border-width;
|
||||
&-wrapper {
|
||||
background-color: @component-background;
|
||||
}
|
||||
@ -14,7 +19,7 @@
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: relative;
|
||||
width: 2px;
|
||||
width: @anchor-border-width;
|
||||
height: 100%;
|
||||
display: block;
|
||||
background-color: @border-color-split;
|
||||
@ -23,10 +28,10 @@
|
||||
&-ball {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 9px;
|
||||
border: 3px solid @primary-color;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid @primary-color;
|
||||
background-color: @component-background;
|
||||
left: 50%;
|
||||
transition: top .3s ease-in-out;
|
||||
@ -42,7 +47,7 @@
|
||||
}
|
||||
|
||||
&-link {
|
||||
padding: 8px 0 8px 18px;
|
||||
padding: 8px 0 8px 16px;
|
||||
line-height: 1;
|
||||
|
||||
&-title {
|
||||
|
@ -1,18 +1,22 @@
|
||||
import React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
export default class InputElement extends React.Component<any, any> {
|
||||
export interface InputElementProps {
|
||||
children: React.ReactElement<any>;
|
||||
}
|
||||
|
||||
export default class InputElement extends React.Component<InputElementProps, any> {
|
||||
private ele: HTMLInputElement;
|
||||
|
||||
focus = () => {
|
||||
this.ele.focus ? this.ele.focus() : (findDOMNode(this.ele) as HTMLInputElement).focus();
|
||||
this.ele.focus ? this.ele.focus() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
|
||||
}
|
||||
blur = () => {
|
||||
this.ele.blur ? this.ele.blur() : (findDOMNode(this.ele) as HTMLInputElement).blur();
|
||||
this.ele.blur ? this.ele.blur() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur();
|
||||
}
|
||||
saveRef = (ele: HTMLInputElement) => {
|
||||
this.ele = ele;
|
||||
const childRef = this.props.children.ref;
|
||||
const { ref: childRef } = this.props.children as any;
|
||||
if (typeof childRef === 'function') {
|
||||
childRef(ele);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
|
||||
style="width:200px;"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
@ -18,7 +18,7 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection__placeholder"
|
||||
style="display:block;user-select:none;-webkit-user-select:none;"
|
||||
style="display:block;user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
input here
|
||||
@ -46,7 +46,7 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none;"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<b />
|
||||
@ -58,11 +58,11 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
|
||||
exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1`] = `
|
||||
<div
|
||||
class="certain-category-search-wrapper"
|
||||
style="width:250px;"
|
||||
style="width:250px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-lg ant-select-lg certain-category-search ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
|
||||
style="width:100%;"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
@ -77,7 +77,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection__placeholder"
|
||||
style="display:block;user-select:none;-webkit-user-select:none;"
|
||||
style="display:block;user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
input here
|
||||
@ -116,7 +116,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none;"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<b />
|
||||
@ -129,7 +129,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
|
||||
exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
|
||||
style="width:200px;"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
@ -152,7 +152,7 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
|
||||
<textarea
|
||||
class="ant-input custom ant-select-search__field"
|
||||
placeholder="input here"
|
||||
style="height:50px;"
|
||||
style="height:50px"
|
||||
/>
|
||||
<span
|
||||
class="ant-select-search__field__mirror"
|
||||
@ -165,7 +165,7 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none;"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<b />
|
||||
@ -177,7 +177,7 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
|
||||
exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
|
||||
style="width:200px;"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
@ -192,7 +192,7 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection__placeholder"
|
||||
style="display:block;user-select:none;-webkit-user-select:none;"
|
||||
style="display:block;user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
try to type \`b\`
|
||||
@ -220,7 +220,7 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none;"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<b />
|
||||
@ -232,7 +232,7 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
|
||||
exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
|
||||
style="width:200px;"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
@ -247,7 +247,7 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection__placeholder"
|
||||
style="display:block;user-select:none;-webkit-user-select:none;"
|
||||
style="display:block;user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
input here
|
||||
@ -275,7 +275,7 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none;"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<b />
|
||||
@ -287,11 +287,11 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly 1`] = `
|
||||
<div
|
||||
class="global-search-wrapper"
|
||||
style="width:300px;"
|
||||
style="width:300px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-lg ant-select-lg global-search ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
|
||||
style="width:100%;"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
@ -306,7 +306,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection__placeholder"
|
||||
style="display:block;user-select:none;-webkit-user-select:none;"
|
||||
style="display:block;user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
input here
|
||||
@ -350,7 +350,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none;"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="unselectable"
|
||||
>
|
||||
<b />
|
||||
|
@ -1,8 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import AutoComplete from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
|
||||
describe('AutoComplete with Custom Input Element Render', () => {
|
||||
focusTest(AutoComplete);
|
||||
|
||||
it('AutoComplete with custom Input render perfectly', () => {
|
||||
const wrapper = mount(
|
||||
<AutoComplete dataSource={['12345', '23456', '34567']}>
|
||||
@ -12,7 +15,7 @@ describe('AutoComplete with Custom Input Element Render', () => {
|
||||
|
||||
expect(wrapper.find('textarea').length).toBe(1);
|
||||
wrapper.find('textarea').simulate('change', { target: { value: '123' } });
|
||||
const dropdownWrapper = mount(wrapper.find('Trigger').node.getComponent());
|
||||
const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||
|
||||
// should not filter data source defaultly
|
||||
expect(dropdownWrapper.find('MenuItem').length).toBe(3);
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import { Option, OptGroup } from 'rc-select';
|
||||
import classNames from 'classnames';
|
||||
import Select, { AbstractSelectProps, SelectValue, OptionProps, OptGroupProps } from '../select';
|
||||
@ -34,7 +34,7 @@ function isSelectOptionOrSelectOptGroup(child: any): Boolean {
|
||||
return child && child.type && (child.type.isSelectOption || child.type.isSelectOptGroup);
|
||||
}
|
||||
|
||||
export default class AutoComplete extends React.Component<AutoCompleteProps, any> {
|
||||
export default class AutoComplete extends React.Component<AutoCompleteProps, {}> {
|
||||
static Option = Option as React.ClassicComponentClass<OptionProps>;
|
||||
static OptGroup = OptGroup as React.ClassicComponentClass<OptGroupProps>;
|
||||
|
||||
@ -47,6 +47,8 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
|
||||
filterOption: false,
|
||||
};
|
||||
|
||||
private select: any;
|
||||
|
||||
getInputElement = () => {
|
||||
const { children } = this.props;
|
||||
const element = children && React.isValidElement(children) && children.type !== Option ?
|
||||
@ -59,6 +61,18 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
|
||||
);
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.select.focus();
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.select.blur();
|
||||
}
|
||||
|
||||
saveSelect = (node: any) => {
|
||||
this.select = node;
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
size, className = '', notFoundContent, prefixCls, optionLabelProp, dataSource, children,
|
||||
@ -106,6 +120,7 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
|
||||
optionLabelProp={optionLabelProp}
|
||||
getInputElement={this.getInputElement}
|
||||
notFoundContent={notFoundContent}
|
||||
ref={this.saveSelect}
|
||||
>
|
||||
{options}
|
||||
</Select>
|
||||
|
@ -7,6 +7,8 @@
|
||||
@autocomplete-prefix-cls: ~"@{select-prefix-cls}-auto-complete";
|
||||
|
||||
.@{autocomplete-prefix-cls} {
|
||||
.reset-component;
|
||||
|
||||
&.@{select-prefix-cls} {
|
||||
.@{select-prefix-cls} {
|
||||
&-selection {
|
||||
|
@ -3,7 +3,7 @@
|
||||
exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
|
||||
<div>
|
||||
<span
|
||||
style="margin-right:24px;"
|
||||
style="margin-right:24px"
|
||||
>
|
||||
<span
|
||||
class="ant-badge"
|
||||
@ -22,7 +22,7 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1100%);-webkit-transform:translateY(-1100%);transform:translateY(-1100%);"
|
||||
style="transition:none;-ms-transform:translateY(-1100%);-webkit-transform:translateY(-1100%);transform:translateY(-1100%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -253,7 +253,7 @@ exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class="ant-avatar ant-avatar-lg ant-avatar-circle"
|
||||
style="background-color:#f56a00;"
|
||||
style="background-color:#f56a00;vertical-align:middle"
|
||||
>
|
||||
<span
|
||||
class="ant-avatar-string"
|
||||
@ -263,7 +263,7 @@ exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
|
||||
</span>
|
||||
<button
|
||||
class="ant-btn ant-btn-sm"
|
||||
style="margin-left:16px;"
|
||||
style="margin-left:16px;vertical-align:middle"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
@ -309,7 +309,7 @@ exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
|
||||
</span>
|
||||
<span
|
||||
class="ant-avatar ant-avatar-circle"
|
||||
style="color:#f56a00;background-color:#fde3cf;"
|
||||
style="color:#f56a00;background-color:#fde3cf"
|
||||
>
|
||||
<span
|
||||
class="ant-avatar-string"
|
||||
@ -319,7 +319,7 @@ exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
|
||||
</span>
|
||||
<span
|
||||
class="ant-avatar ant-avatar-circle ant-avatar-icon"
|
||||
style="background-color:#87d068;"
|
||||
style="background-color:#87d068"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-user"
|
||||
|
@ -37,8 +37,12 @@ class Autoset extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Avatar style={{ backgroundColor: this.state.color }} size="large">{this.state.user}</Avatar>
|
||||
<Button size="small" style={{ marginLeft: 16 }} onClick={this.changeUser}>Change</Button>
|
||||
<Avatar style={{ backgroundColor: this.state.color, verticalAlign: 'middle' }} size="large">
|
||||
{this.state.user}
|
||||
</Avatar>
|
||||
<Button size="small" style={{ marginLeft: 16, verticalAlign: 'middle' }} onClick={this.changeUser}>
|
||||
Change
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import Icon from '../icon';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@avatar-prefix-cls: ~"@{ant-prefix}-avatar";
|
||||
|
||||
.@{avatar-prefix-cls} {
|
||||
.reset-component;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
background: @avatar-bg;
|
||||
|
@ -4,7 +4,7 @@ exports[`renders ./components/back-top/demo/basic.md correctly 1`] = `
|
||||
<div>
|
||||
Scroll down to see the bottom-right
|
||||
<strong
|
||||
style="color:rgba(64, 64, 64, 0.6);"
|
||||
style="color:rgba(64, 64, 64, 0.6)"
|
||||
>
|
||||
gray
|
||||
</strong>
|
||||
@ -16,7 +16,7 @@ exports[`renders ./components/back-top/demo/custom.md correctly 1`] = `
|
||||
<div>
|
||||
Scroll down to see the bottom-right
|
||||
<strong
|
||||
style="color:#1088e9;"
|
||||
style="color:#1088e9"
|
||||
>
|
||||
blue
|
||||
</strong>
|
||||
|
@ -15,7 +15,7 @@ describe('BackTop', () => {
|
||||
const wrapper = mount(<BackTop visibilityHeight={-1} />);
|
||||
document.documentElement.scrollTop = 400;
|
||||
// trigger scroll manually
|
||||
wrapper.node.handleScroll();
|
||||
wrapper.instance().handleScroll();
|
||||
jest.runAllTimers();
|
||||
wrapper.find('.ant-back-top').simulate('click');
|
||||
jest.runAllTimers();
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import Animate from 'rc-animate';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import Icon from '../icon';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
|
||||
|
||||
@ -109,7 +108,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
|
||||
const defaultElement = (
|
||||
<div className={`${prefixCls}-content`}>
|
||||
<Icon className={`${prefixCls}-icon`} type="to-top" />
|
||||
<div className={`${prefixCls}-icon`} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@backtop-prefix-cls: ~"@{ant-prefix}-back-top";
|
||||
|
||||
.@{backtop-prefix-cls} {
|
||||
.reset-component;
|
||||
z-index: @zindex-back-top;
|
||||
position: fixed;
|
||||
right: 100px;
|
||||
@ -19,6 +21,7 @@
|
||||
color: @back-top-color;
|
||||
text-align: center;
|
||||
transition: all .3s @ease-in-out;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background-color: @back-top-hover-bg;
|
||||
@ -27,7 +30,9 @@
|
||||
}
|
||||
|
||||
&-icon {
|
||||
font-size: 20px;
|
||||
margin-top: 10px;
|
||||
margin: 12px auto;
|
||||
width: 14px;
|
||||
height: 16px;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC) ~"100%/100%" no-repeat;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import { createElement, Component } from 'react';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
|
||||
function getNumberArray(num) {
|
||||
function getNumberArray(num: string | number | undefined) {
|
||||
return num ?
|
||||
num.toString()
|
||||
.split('')
|
||||
@ -21,7 +21,12 @@ export interface ScrollNumberProps {
|
||||
title?: string | number;
|
||||
}
|
||||
|
||||
export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
export interface ScrollNumberState {
|
||||
animateStarted?: boolean;
|
||||
count?: string | number;
|
||||
}
|
||||
|
||||
export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-scroll-number',
|
||||
count: null,
|
||||
@ -31,7 +36,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
|
||||
lastCount: any;
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: ScrollNumberProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
animateStarted: true,
|
||||
@ -39,14 +44,14 @@ export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
};
|
||||
}
|
||||
|
||||
getPositionByNum(num, i) {
|
||||
getPositionByNum(num: number, i: number) {
|
||||
if (this.state.animateStarted) {
|
||||
return 10 + num;
|
||||
}
|
||||
const currentDigit = getNumberArray(this.state.count)[i];
|
||||
const lastDigit = getNumberArray(this.lastCount)[i];
|
||||
// 同方向则在同一侧切换数字
|
||||
if (this.state.count > this.lastCount) {
|
||||
if (this.state.count! > this.lastCount) {
|
||||
if (currentDigit >= lastDigit) {
|
||||
return 10 + num;
|
||||
}
|
||||
@ -58,7 +63,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
return num;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps: ScrollNumberProps) {
|
||||
if ('count' in nextProps) {
|
||||
if (this.state.count === nextProps.count) {
|
||||
return;
|
||||
@ -85,7 +90,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
}
|
||||
}
|
||||
|
||||
renderNumberList(position) {
|
||||
renderNumberList(position: number) {
|
||||
const childrenToReturn: React.ReactElement<any>[] = [];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const currentClassName = (position === i) ? 'current' : '';
|
||||
@ -94,7 +99,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
return childrenToReturn;
|
||||
}
|
||||
|
||||
renderCurrentNumber(num, i) {
|
||||
renderCurrentNumber(num: number, i: number) {
|
||||
const position = this.getPositionByNum(num, i);
|
||||
const removeTransition = this.state.animateStarted ||
|
||||
(getNumberArray(this.lastCount)[i] === undefined);
|
||||
@ -112,7 +117,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
|
||||
renderNumberElement() {
|
||||
const state = this.state;
|
||||
if (!state.count || isNaN(state.count)) {
|
||||
if (!state.count || isNaN(state.count as number)) {
|
||||
return state.count;
|
||||
}
|
||||
return getNumberArray(state.count)
|
||||
|
@ -16,7 +16,7 @@ exports[`renders ./components/badge/demo/basic.md correctly 1`] = `
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%);"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -206,7 +206,7 @@ exports[`renders ./components/badge/demo/change.md correctly 1`] = `
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%);"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -383,7 +383,7 @@ exports[`renders ./components/badge/demo/change.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="margin-top:10px;"
|
||||
style="margin-top:10px"
|
||||
>
|
||||
<span
|
||||
class="ant-badge"
|
||||
@ -456,7 +456,7 @@ exports[`renders ./components/badge/demo/link.md correctly 1`] = `
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%);"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -620,13 +620,13 @@ exports[`renders ./components/badge/demo/no-wrapper.md correctly 1`] = `
|
||||
class="ant-badge ant-badge-not-a-wrapper"
|
||||
>
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
class="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show="true"
|
||||
title="25"
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1200%);-webkit-transform:translateY(-1200%);transform:translateY(-1200%);"
|
||||
style="transition:none;-ms-transform:translateY(-1200%);-webkit-transform:translateY(-1200%);transform:translateY(-1200%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -781,7 +781,7 @@ exports[`renders ./components/badge/demo/no-wrapper.md correctly 1`] = `
|
||||
</span>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%);"
|
||||
style="transition:none;-ms-transform:translateY(-1500%);-webkit-transform:translateY(-1500%);transform:translateY(-1500%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -942,12 +942,12 @@ exports[`renders ./components/badge/demo/no-wrapper.md correctly 1`] = `
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
data-show="true"
|
||||
style="background-color:#fff;color:#999;box-shadow:0 0 0 1px #d9d9d9 inset;"
|
||||
style="background-color:#fff;color:#999;box-shadow:0 0 0 1px #d9d9d9 inset"
|
||||
title="4"
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1400%);-webkit-transform:translateY(-1400%);transform:translateY(-1400%);"
|
||||
style="transition:none;-ms-transform:translateY(-1400%);-webkit-transform:translateY(-1400%);transform:translateY(-1400%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -1106,9 +1106,9 @@ exports[`renders ./components/badge/demo/no-wrapper.md correctly 1`] = `
|
||||
class="ant-badge ant-badge-not-a-wrapper"
|
||||
>
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
class="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show="true"
|
||||
style="background-color:#87d068;"
|
||||
style="background-color:#52c41a"
|
||||
title="109"
|
||||
>
|
||||
99+
|
||||
@ -1127,13 +1127,13 @@ exports[`renders ./components/badge/demo/overflow.md correctly 1`] = `
|
||||
href="#"
|
||||
/>
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
class="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show="true"
|
||||
title="99"
|
||||
>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1900%);-webkit-transform:translateY(-1900%);transform:translateY(-1900%);"
|
||||
style="transition:none;-ms-transform:translateY(-1900%);-webkit-transform:translateY(-1900%);transform:translateY(-1900%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -1288,7 +1288,7 @@ exports[`renders ./components/badge/demo/overflow.md correctly 1`] = `
|
||||
</span>
|
||||
<span
|
||||
class="ant-scroll-number-only"
|
||||
style="transition:none;-ms-transform:translateY(-1900%);-webkit-transform:translateY(-1900%);transform:translateY(-1900%);"
|
||||
style="transition:none;-ms-transform:translateY(-1900%);-webkit-transform:translateY(-1900%);transform:translateY(-1900%)"
|
||||
>
|
||||
<p
|
||||
class=""
|
||||
@ -1451,7 +1451,7 @@ exports[`renders ./components/badge/demo/overflow.md correctly 1`] = `
|
||||
href="#"
|
||||
/>
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
class="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show="true"
|
||||
title="100"
|
||||
>
|
||||
@ -1466,7 +1466,7 @@ exports[`renders ./components/badge/demo/overflow.md correctly 1`] = `
|
||||
href="#"
|
||||
/>
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
class="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show="true"
|
||||
title="99"
|
||||
>
|
||||
@ -1481,7 +1481,7 @@ exports[`renders ./components/badge/demo/overflow.md correctly 1`] = `
|
||||
href="#"
|
||||
/>
|
||||
<sup
|
||||
class="ant-scroll-number ant-badge-count"
|
||||
class="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show="true"
|
||||
title="1000"
|
||||
>
|
||||
|
@ -30,12 +30,12 @@ ReactDOM.render(
|
||||
|
||||
<style>
|
||||
.ant-badge:not(.ant-badge-status) {
|
||||
margin-right: 16px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.head-example {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 6px;
|
||||
border-radius: 4px;
|
||||
background: #eee;
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ ReactDOM.render(
|
||||
<div>
|
||||
<Badge count={25} />
|
||||
<Badge count={4} style={{ backgroundColor: '#fff', color: '#999', boxShadow: '0 0 0 1px #d9d9d9 inset' }} />
|
||||
<Badge count={109} style={{ backgroundColor: '#87d068' }} />
|
||||
<Badge count={109} style={{ backgroundColor: '#52c41a' }} />
|
||||
</div>
|
||||
, mountNode);
|
||||
````
|
||||
|
@ -30,3 +30,4 @@ Badge normally appears in proximity to notifications or user avatars with eye-ca
|
||||
| showZero | Whether to show badge when `count` is zero | boolean | `false` |
|
||||
| status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | `''` |
|
||||
| text | If `status` is set, `text` sets the display text of the status `dot` | string | `''` |
|
||||
| offset | set offset of the badge dot, like [x, y] | [number, number] | - |
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Animate from 'rc-animate';
|
||||
import ScrollNumber from './ScrollNumber';
|
||||
import classNames from 'classnames';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
export { ScrollNumberProps } from './ScrollNumber';
|
||||
|
||||
export interface BadgeProps {
|
||||
/** Number to show in badge */
|
||||
@ -19,6 +20,7 @@ export interface BadgeProps {
|
||||
className?: string;
|
||||
status?: 'success' | 'processing' | 'default' | 'error' | 'warning';
|
||||
text?: string;
|
||||
offset?: [number | string, number | string];
|
||||
}
|
||||
|
||||
export default class Badge extends React.Component<BadgeProps, any> {
|
||||
@ -54,6 +56,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
dot,
|
||||
status,
|
||||
text,
|
||||
offset,
|
||||
...restProps,
|
||||
} = this.props;
|
||||
const isDot = dot || status;
|
||||
@ -66,27 +69,29 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
const isZero = displayCount === '0' || displayCount === 0;
|
||||
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
|
||||
const hidden = (isEmpty || (isZero && !showZero)) && !isDot;
|
||||
const statusCls = classNames({
|
||||
[`${prefixCls}-status-dot`]: !!status,
|
||||
[`${prefixCls}-status-${status}`]: !!status,
|
||||
});
|
||||
const scrollNumberCls = classNames({
|
||||
[`${prefixCls}-dot`]: isDot,
|
||||
[`${prefixCls}-count`]: !isDot,
|
||||
[`${prefixCls}-multiple-words`]: count && count.toString && count.toString().length > 1,
|
||||
[`${prefixCls}-status-${status}`]: !!status,
|
||||
});
|
||||
const badgeCls = classNames(className, prefixCls, {
|
||||
[`${prefixCls}-status`]: !!status,
|
||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||
});
|
||||
|
||||
warning(
|
||||
!(children && status),
|
||||
'`Badge[children]` and `Badge[status]` cannot be used at the same time.',
|
||||
);
|
||||
const styleWithOffset = offset ? {
|
||||
marginTop: offset[0],
|
||||
marginLeft: offset[1],
|
||||
...style,
|
||||
} : style;
|
||||
// <Badge status="success" />
|
||||
if (!children && status) {
|
||||
const statusCls = classNames({
|
||||
[`${prefixCls}-status-dot`]: !!status,
|
||||
[`${prefixCls}-status-${status}`]: true,
|
||||
});
|
||||
return (
|
||||
<span className={badgeCls}>
|
||||
<span className={badgeCls} style={styleWithOffset}>
|
||||
<span className={statusCls} />
|
||||
<span className={`${prefixCls}-status-text`}>{text}</span>
|
||||
</span>
|
||||
@ -100,7 +105,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
className={scrollNumberCls}
|
||||
count={displayCount}
|
||||
title={count}
|
||||
style={style}
|
||||
style={styleWithOffset}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -31,3 +31,4 @@ title: Badge
|
||||
| showZero | 当数值为 0 时,是否展示 Badge | boolean | false |
|
||||
| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' |
|
||||
| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' |
|
||||
| offset | 设置状态点的位置偏移,格式为 [x, y] | [number, number] | - |
|
||||
|
@ -1,9 +1,11 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@badge-prefix-cls: ~"@{ant-prefix}-badge";
|
||||
@number-prefix-cls: ~"@{ant-prefix}-scroll-number";
|
||||
|
||||
.@{badge-prefix-cls} {
|
||||
.reset-component;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
@ -24,13 +26,17 @@
|
||||
font-size: @badge-font-size;
|
||||
white-space: nowrap;
|
||||
transform-origin: -10% center;
|
||||
font-family: tahoma;
|
||||
box-shadow: 0 0 0 1px #fff;
|
||||
a,
|
||||
a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&-multiple-words {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
&-dot {
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
@ -49,16 +55,19 @@
|
||||
vertical-align: baseline;
|
||||
|
||||
&-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
width: @badge-status-size;
|
||||
height: @badge-status-size;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
&-success {
|
||||
background-color: @success-color;
|
||||
}
|
||||
&-processing {
|
||||
background-color: @primary-color;
|
||||
background-color: @processing-color;
|
||||
position: relative;
|
||||
&:after {
|
||||
position: absolute;
|
||||
@ -67,7 +76,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
border: 1px solid @primary-color;
|
||||
border: 1px solid @processing-color;
|
||||
content: '';
|
||||
animation: antStatusProcessing 1.2s infinite ease-in-out;
|
||||
}
|
||||
@ -99,7 +108,7 @@
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
&-not-a-wrapper &-count {
|
||||
&-not-a-wrapper .@{ant-prefix}-scroll-number {
|
||||
top: auto;
|
||||
display: block;
|
||||
position: relative;
|
||||
@ -126,6 +135,7 @@
|
||||
height: @badge-height;
|
||||
> p {
|
||||
height: @badge-height;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,26 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { cloneElement } from 'react';
|
||||
import warning from '../_util/warning';
|
||||
import BreadcrumbItem from './BreadcrumbItem';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export interface Route {
|
||||
path: string;
|
||||
breadcrumbName: string;
|
||||
}
|
||||
|
||||
export interface BreadcrumbProps {
|
||||
prefixCls?: string;
|
||||
routes?: Array<any>;
|
||||
params?: Object;
|
||||
routes?: Route[];
|
||||
params?: any;
|
||||
separator?: React.ReactNode;
|
||||
itemRender?: (route: any, params: any, routes: Array<any>, paths: Array<string>) => React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function getBreadcrumbName(route, params) {
|
||||
function getBreadcrumbName(route: Route, params: any) {
|
||||
if (!route.breadcrumbName) {
|
||||
return null;
|
||||
}
|
||||
@ -27,7 +32,7 @@ function getBreadcrumbName(route, params) {
|
||||
return name;
|
||||
}
|
||||
|
||||
function defaultItemRender(route, params, routes, paths) {
|
||||
function defaultItemRender(route: Route, params: any, routes: Route[], paths: string[]) {
|
||||
const isLastItem = routes.indexOf(route) === routes.length - 1;
|
||||
const name = getBreadcrumbName(route, params);
|
||||
return isLastItem
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export interface BreadcrumbItemProps {
|
||||
|
@ -1,11 +1,3 @@
|
||||
import { render } from 'enzyme';
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
import routerDemo from '../demo/router.md';
|
||||
|
||||
demoTest('breadcrumb', { skip: ['router.md', 'router-4.md'] });
|
||||
|
||||
const testMethod = typeof window !== 'undefined' ? test : test.skip;
|
||||
testMethod('renders ./components/breadcrumb/demo/router.md correctly', () => {
|
||||
const wrapper = render(routerDemo);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,5 +1,8 @@
|
||||
import Breadcrumb from './Breadcrumb';
|
||||
import BreadcrumbItem from './BreadcrumbItem';
|
||||
|
||||
export { BreadcrumbProps } from './Breadcrumb';
|
||||
export { BreadcrumbItemProps } from './BreadcrumbItem';
|
||||
|
||||
Breadcrumb.Item = BreadcrumbItem;
|
||||
export default Breadcrumb;
|
||||
|
@ -1,13 +1,18 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@breadcrumb-prefix-cls: ~"@{ant-prefix}-breadcrumb";
|
||||
|
||||
.@{breadcrumb-prefix-cls} {
|
||||
color: @text-color;
|
||||
font-size: @font-size-base;
|
||||
.reset-component;
|
||||
color: @text-color-secondary;
|
||||
|
||||
.@{iconfont-css-prefix} {
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @text-color;
|
||||
color: @text-color-secondary;
|
||||
transition: color .3s;
|
||||
&:hover {
|
||||
color: @primary-5;
|
||||
@ -15,7 +20,6 @@
|
||||
}
|
||||
|
||||
& > span:last-child {
|
||||
font-weight: 500;
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
@ -24,8 +28,8 @@
|
||||
}
|
||||
|
||||
&-separator {
|
||||
margin: 0 8px;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
margin: 0 @padding-xs;
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
|
||||
&-link {
|
||||
|
@ -251,7 +251,7 @@ exports[`renders ./components/button/demo/disabled.md correctly 1`] = `
|
||||
|
||||
exports[`renders ./components/button/demo/ghost.md correctly 1`] = `
|
||||
<div
|
||||
style="background:rgb(190, 200, 200);padding:26px 16px 16px;"
|
||||
style="background:rgb(190, 200, 200);padding:26px 16px 16px"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-background-ghost"
|
||||
|
@ -49,3 +49,15 @@ exports[`Button renders correctly 1`] = `
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`Button should support link button 1`] = `
|
||||
<a
|
||||
class="ant-btn"
|
||||
href="http://ant.design"
|
||||
target="_blank"
|
||||
>
|
||||
<span>
|
||||
link button
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
@ -52,7 +52,7 @@ describe('Button', () => {
|
||||
<DefaultButton />
|
||||
);
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.hasClass('ant-btn-loading')).toBe(true);
|
||||
expect(wrapper.find('.ant-btn-loading').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should change loading state with delay', () => {
|
||||
@ -74,4 +74,11 @@ describe('Button', () => {
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.hasClass('ant-btn-loading')).toBe(false);
|
||||
});
|
||||
|
||||
it('should support link button', () => {
|
||||
const wrapper = mount(
|
||||
<Button target="_blank" href="http://ant.design">link button</Button>
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export type ButtonSize = 'small' | 'large';
|
||||
import { ButtonSize } from './button';
|
||||
|
||||
export interface ButtonGroupProps {
|
||||
size?: ButtonSize;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
@ -52,6 +52,9 @@ export interface ButtonProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
ghost?: boolean;
|
||||
target?: string;
|
||||
href?: string;
|
||||
download?: string;
|
||||
}
|
||||
|
||||
export default class Button extends React.Component<ButtonProps, any> {
|
||||
@ -95,7 +98,7 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
}
|
||||
|
||||
if (typeof loading !== 'boolean' && loading && loading.delay) {
|
||||
this.delayTimeout = setTimeout(() => this.setState({ loading }), loading.delay);
|
||||
this.delayTimeout = window.setTimeout(() => this.setState({ loading }), loading.delay);
|
||||
} else {
|
||||
this.setState({ loading });
|
||||
}
|
||||
@ -114,7 +117,7 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
// Add click effect
|
||||
this.setState({ clicked: true });
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => this.setState({ clicked: false }), 500);
|
||||
this.timeout = window.setTimeout(() => this.setState({ clicked: false }), 500);
|
||||
|
||||
const onClick = this.props.onClick;
|
||||
if (onClick) {
|
||||
@ -142,6 +145,8 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
break;
|
||||
}
|
||||
|
||||
const ComponentProp = others.href ? 'a' : 'button';
|
||||
|
||||
const classes = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-${type}`]: type,
|
||||
[`${prefixCls}-${shape}`]: shape,
|
||||
@ -155,17 +160,17 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
const iconType = loading ? 'loading' : icon;
|
||||
const iconNode = iconType ? <Icon type={iconType} /> : null;
|
||||
const needInserted = React.Children.count(children) === 1 && (!iconType || iconType === 'loading');
|
||||
const kids = React.Children.map(children, child => insertSpace(child, needInserted));
|
||||
const kids = children ? React.Children.map(children, child => insertSpace(child, needInserted)) : null;
|
||||
|
||||
return (
|
||||
<button
|
||||
<ComponentProp
|
||||
{...omit(others, ['loading'])}
|
||||
type={htmlType || 'button'}
|
||||
type={others.href ? undefined : (htmlType || 'button')}
|
||||
className={classes}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
{iconNode}{kids}
|
||||
</button>
|
||||
</ComponentProp>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,13 @@ To get a customized button, just set `type`/`shape`/`size`/`loading`/`disabled`.
|
||||
| size | can be set to `small` `large` or omitted | string | `default` |
|
||||
| type | can be set to `primary` `ghost` `dashed` `danger`(added in 2.7) or omitted (meaning `default`) | string | `default` |
|
||||
| onClick | set the handler to handle `click` event | function | - |
|
||||
| href | redirect url of link button | string | - |
|
||||
| target | same as target attribute of a, works when href is specified | string | - |
|
||||
|
||||
`<Button>Hello world!</Button>` will be rendered into `<button><span>Hello world!</span></button>`, and all the properties which are not listed above will be transferred to the `<button>` tag.
|
||||
|
||||
`<Button href="http://example.com">Hello world!</Button>` will be rendered into `<a href="http://example.com"><span>Hello world!</span></a>`.
|
||||
|
||||
<style>
|
||||
[id^=components-button-demo-] .ant-btn {
|
||||
margin-right: 8px;
|
||||
|
@ -1,5 +1,8 @@
|
||||
import Button from './button';
|
||||
import ButtonGroup from './button-group';
|
||||
|
||||
export { ButtonProps, ButtonShape, ButtonSize, ButtonType } from './button';
|
||||
export { ButtonGroupProps } from './button-group';
|
||||
|
||||
Button.Group = ButtonGroup;
|
||||
export default Button;
|
||||
|
@ -27,9 +27,14 @@ subtitle: 按钮
|
||||
| size | 设置按钮大小,可选值为 `small` `large` 或者不设 | string | `default` |
|
||||
| type | 设置按钮类型,可选值为 `primary` `dashed` `danger`(版本 2.7 中增加) 或者不设 | string | - |
|
||||
| onClick | `click` 事件的 handler | function | - |
|
||||
| href | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致 | string | - |
|
||||
| target | 相当于 a 链接的 target 属性,href 存在时生效 | string | - |
|
||||
|
||||
|
||||
`<Button>Hello world!</Button>` 最终会被渲染为 `<button><span>Hello world!</span></button>`,并且除了上表中的属性,其它属性都会直接传到 `<button></button>`。
|
||||
|
||||
`<Button href="http://example.com">Hello world!</Button>` 则会渲染为 `<a href="http://example.com"><span>Hello world!</span></a>`。
|
||||
|
||||
<style>
|
||||
[id^="components-button-demo-"] .ant-btn {
|
||||
margin-right: 8px;
|
||||
|
@ -12,6 +12,7 @@
|
||||
// Button styles
|
||||
// -----------------------------
|
||||
.@{btn-prefix-cls} {
|
||||
.reset-component;
|
||||
.btn;
|
||||
.btn-default;
|
||||
|
||||
@ -127,7 +128,7 @@
|
||||
// To ensure that a space will be placed between character and `Icon`.
|
||||
> .@{iconfont-css-prefix} + span,
|
||||
> span + .@{iconfont-css-prefix} {
|
||||
margin-left: 0.5em;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&-clicked:after {
|
||||
@ -173,3 +174,13 @@
|
||||
border-width: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
a.@{btn-prefix-cls} {
|
||||
line-height: @btn-height-base - 2px;
|
||||
&-lg {
|
||||
line-height: @btn-height-lg - 2px;
|
||||
}
|
||||
&-sm {
|
||||
line-height: @btn-height-sm - 2px;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.button-color(@primary-color; @background; @primary-color);
|
||||
.button-color(@primary-5; @background; @primary-5);
|
||||
}
|
||||
|
||||
&:active,
|
||||
@ -56,12 +56,12 @@
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.button-color(@btn-primary-color; @color; @color;);
|
||||
.button-color(@btn-primary-color; ~`colorPalette("@{color}", 5)`; ~`colorPalette("@{color}", 5)`);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
.button-color(@btn-primary-color; ~`colorPalette("@{color}", 7)`; ~`colorPalette("@{color}", 7)`;);
|
||||
.button-color(@btn-primary-color; ~`colorPalette("@{color}", 7)`; ~`colorPalette("@{color}", 7)`);
|
||||
}
|
||||
|
||||
.button-disabled();
|
||||
@ -185,7 +185,7 @@
|
||||
}
|
||||
|
||||
&-sm {
|
||||
.button-size(@btn-height-sm; @btn-padding-sm; @font-size-base; @btn-border-radius-sm);
|
||||
.button-size(@btn-height-sm; @btn-padding-sm; @btn-font-size-sm; @btn-border-radius-sm);
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,8 +251,6 @@
|
||||
|
||||
.@{btnClassName}:not(:first-child):not(:last-child) {
|
||||
border-radius: 0;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
> .@{btnClassName}:first-child {
|
||||
@ -260,14 +258,12 @@
|
||||
&:not(:last-child) {
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
> .@{btnClassName}:last-child:not(:first-child) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
& > & {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import * as React from 'react';
|
||||
import * as moment from 'moment';
|
||||
import { PREFIX_CLS } from './Constants';
|
||||
import Select from '../select';
|
||||
import { Group, Button } from '../radio';
|
||||
@ -12,7 +12,7 @@ export interface HeaderProps {
|
||||
yearSelectOffset?: number;
|
||||
yearSelectTotal?: number;
|
||||
type?: string;
|
||||
onValueChange?: (value) => void;
|
||||
onValueChange?: (value: moment.Moment) => void;
|
||||
onTypeChange?: (type: string) => void;
|
||||
value: any;
|
||||
}
|
||||
@ -24,9 +24,9 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
yearSelectTotal: 20,
|
||||
};
|
||||
|
||||
calenderHeaderNode: any;
|
||||
private calenderHeaderNode: HTMLDivElement;
|
||||
|
||||
getYearSelectElement(year) {
|
||||
getYearSelectElement(year: number) {
|
||||
const { yearSelectOffset, yearSelectTotal, locale, prefixCls, fullscreen } = this.props;
|
||||
const start = year - (yearSelectOffset as number);
|
||||
const end = start + (yearSelectTotal as number);
|
||||
@ -61,7 +61,7 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
return months;
|
||||
}
|
||||
|
||||
getMonthSelectElement(month, months) {
|
||||
getMonthSelectElement(month: number, months: number[]) {
|
||||
const props = this.props;
|
||||
const { prefixCls, fullscreen } = props;
|
||||
const options: React.ReactElement<any>[] = [];
|
||||
@ -84,7 +84,7 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
);
|
||||
}
|
||||
|
||||
onYearChange = (year) => {
|
||||
onYearChange = (year: string) => {
|
||||
const newValue = this.props.value.clone();
|
||||
newValue.year(parseInt(year, 10));
|
||||
|
||||
@ -94,7 +94,7 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
}
|
||||
}
|
||||
|
||||
onMonthChange = (month) => {
|
||||
onMonthChange = (month: string) => {
|
||||
const newValue = this.props.value.clone();
|
||||
newValue.month(parseInt(month, 10));
|
||||
const onValueChange = this.props.onValueChange;
|
||||
@ -103,14 +103,14 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
}
|
||||
}
|
||||
|
||||
onTypeChange = (e) => {
|
||||
onTypeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const onTypeChange = this.props.onTypeChange;
|
||||
if (onTypeChange) {
|
||||
onTypeChange(e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
getCalenderHeaderNode = (node) => {
|
||||
getCalenderHeaderNode = (node: HTMLDivElement) => {
|
||||
this.calenderHeaderNode = node;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
title:
|
||||
zh-CN: 卡片模式
|
||||
en-US: Card
|
||||
---
|
||||
@ -21,7 +21,7 @@ function onPanelChange(value, mode) {
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<div style={{ width: 290, border: '1px solid #d9d9d9', borderRadius: 4 }}>
|
||||
<div style={{ width: 300, border: '1px solid #d9d9d9', borderRadius: 4 }}>
|
||||
<Calendar fullscreen={false} onPanelChange={onPanelChange} />
|
||||
</div>
|
||||
, mountNode);
|
||||
|
@ -14,7 +14,7 @@ title:
|
||||
This component can be rendered by using `dateCellRender` and `monthCellRender` with the data you need.
|
||||
|
||||
````jsx
|
||||
import { Calendar } from 'antd';
|
||||
import { Calendar, Badge } from 'antd';
|
||||
|
||||
function getListData(value) {
|
||||
let listData;
|
||||
@ -22,18 +22,18 @@ function getListData(value) {
|
||||
case 8:
|
||||
listData = [
|
||||
{ type: 'warning', content: 'This is warning event.' },
|
||||
{ type: 'normal', content: 'This is usual event.' },
|
||||
{ type: 'success', content: 'This is usual event.' },
|
||||
]; break;
|
||||
case 10:
|
||||
listData = [
|
||||
{ type: 'warning', content: 'This is warning event.' },
|
||||
{ type: 'normal', content: 'This is usual event.' },
|
||||
{ type: 'success', content: 'This is usual event.' },
|
||||
{ type: 'error', content: 'This is error event.' },
|
||||
]; break;
|
||||
case 15:
|
||||
listData = [
|
||||
{ type: 'warning', content: 'This is warning event' },
|
||||
{ type: 'normal', content: 'This is very long usual event。。....' },
|
||||
{ type: 'success', content: 'This is very long usual event。。....' },
|
||||
{ type: 'error', content: 'This is error event 1.' },
|
||||
{ type: 'error', content: 'This is error event 2.' },
|
||||
{ type: 'error', content: 'This is error event 3.' },
|
||||
@ -51,8 +51,7 @@ function dateCellRender(value) {
|
||||
{
|
||||
listData.map(item => (
|
||||
<li key={item.content}>
|
||||
<span className={`event-${item.type}`}>●</span>
|
||||
{item.content}
|
||||
<Badge status={item.type} text={item.content} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
@ -83,42 +82,20 @@ ReactDOM.render(
|
||||
|
||||
````css
|
||||
.events {
|
||||
line-height: 24px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.events li {
|
||||
color: #999;
|
||||
.events .ant-badge-status {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.events li span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.events li span:first-child {
|
||||
font-size: 9px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.event-warning {
|
||||
color: #fac450;
|
||||
}
|
||||
|
||||
.event-normal {
|
||||
color: #108ee9;
|
||||
}
|
||||
|
||||
.event-error {
|
||||
color: #f50;
|
||||
}
|
||||
|
||||
.notes-month {
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
}
|
||||
.notes-month section {
|
||||
font-size: 28px;
|
||||
|
@ -16,11 +16,10 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
|
||||
**Note:** Part of the Calendar's locale is read from `value`. So, please set the locale of `moment` correctly.
|
||||
|
||||
```jsx
|
||||
import moment from 'moment';
|
||||
|
||||
// It's recommended to set locale in entry file globaly.
|
||||
import 'moment/locale/zh-cn';
|
||||
moment.locale('zh-cn');
|
||||
// The default locale is en-US, if you want to use other locale, just set locale in entry file globaly.
|
||||
// import moment from 'moment';
|
||||
// import 'moment/locale/zh-cn';
|
||||
// moment.locale('zh-cn');
|
||||
|
||||
<Calendar
|
||||
dateCellRender={dateCellRender}
|
||||
|
@ -1,27 +1,25 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import * as moment from 'moment';
|
||||
import FullCalendar from 'rc-calendar/lib/FullCalendar';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import { PREFIX_CLS } from './Constants';
|
||||
import Header from './Header';
|
||||
import { getComponentLocale, getLocaleCode } from '../_util/getLocale';
|
||||
import callMoment from '../_util/callMoment';
|
||||
|
||||
declare const require: Function;
|
||||
|
||||
export { HeaderProps } from './Header';
|
||||
|
||||
function noop() { return null; }
|
||||
|
||||
function zerofixed(v) {
|
||||
function zerofixed(v: number) {
|
||||
if (v < 10) {
|
||||
return `0${v}`;
|
||||
}
|
||||
return `${v}`;
|
||||
}
|
||||
|
||||
export interface CalendarContext {
|
||||
antLocale?: {
|
||||
Calendar?: any,
|
||||
};
|
||||
}
|
||||
|
||||
export type CalendarMode = 'month' | 'year';
|
||||
|
||||
export interface CalendarProps {
|
||||
@ -43,7 +41,7 @@ export interface CalendarProps {
|
||||
}
|
||||
|
||||
export interface CalendarState {
|
||||
value?: moment.Moment;
|
||||
value: moment.Moment;
|
||||
mode?: CalendarMode;
|
||||
}
|
||||
|
||||
@ -72,18 +70,10 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
antLocale: PropTypes.object,
|
||||
};
|
||||
constructor(props: CalendarProps) {
|
||||
super(props);
|
||||
|
||||
context: CalendarContext;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
// Make sure that moment locale had be set correctly.
|
||||
getComponentLocale(props, context, 'Calendar', () => require('./locale/zh_CN'));
|
||||
|
||||
const value = props.value || props.defaultValue || moment();
|
||||
const value = props.value || props.defaultValue || callMoment(moment);
|
||||
if (!moment.isMoment(value)) {
|
||||
throw new Error(
|
||||
'The value/defaultValue of Calendar must be a moment object after `antd@2.0`, ' +
|
||||
@ -99,12 +89,12 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
componentWillReceiveProps(nextProps: CalendarProps) {
|
||||
if ('value' in nextProps) {
|
||||
this.setState({
|
||||
value: nextProps.value,
|
||||
value: nextProps.value!,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
monthCellRender = (value) => {
|
||||
monthCellRender = (value: moment.Moment) => {
|
||||
const { prefixCls, monthCellRender = noop as Function } = this.props;
|
||||
return (
|
||||
<div className={`${prefixCls}-month`}>
|
||||
@ -118,7 +108,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
);
|
||||
}
|
||||
|
||||
dateCellRender = (value) => {
|
||||
dateCellRender = (value: moment.Moment) => {
|
||||
const { prefixCls, dateCellRender = noop as Function } = this.props;
|
||||
return (
|
||||
<div className={`${prefixCls}-date`}>
|
||||
@ -132,7 +122,12 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
);
|
||||
}
|
||||
|
||||
setValue = (value, way: 'select' | 'changePanel') => {
|
||||
getDefaultLocale() {
|
||||
const locale = require('./locale/en_US');
|
||||
return locale.default || locale;
|
||||
}
|
||||
|
||||
setValue = (value: moment.Moment, way: 'select' | 'changePanel') => {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
@ -145,7 +140,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
}
|
||||
}
|
||||
|
||||
setType = (type) => {
|
||||
setType = (type: string) => {
|
||||
const mode = (type === 'date') ? 'month' : 'year';
|
||||
if (this.state.mode !== mode) {
|
||||
this.setState({ mode });
|
||||
@ -153,35 +148,33 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
}
|
||||
}
|
||||
|
||||
onHeaderValueChange = (value) => {
|
||||
onHeaderValueChange = (value: moment.Moment) => {
|
||||
this.setValue(value, 'changePanel');
|
||||
}
|
||||
|
||||
onHeaderTypeChange = (type) => {
|
||||
onHeaderTypeChange = (type: string) => {
|
||||
this.setType(type);
|
||||
}
|
||||
|
||||
onPanelChange(value, mode) {
|
||||
onPanelChange(value: moment.Moment, mode: CalendarMode | undefined) {
|
||||
const { onPanelChange } = this.props;
|
||||
if (onPanelChange) {
|
||||
onPanelChange(value, mode);
|
||||
}
|
||||
}
|
||||
|
||||
onSelect = (value) => {
|
||||
onSelect = (value: moment.Moment) => {
|
||||
this.setValue(value, 'select');
|
||||
}
|
||||
|
||||
render() {
|
||||
const { state, props, context } = this;
|
||||
renderCalendar = (locale: any, localeCode: string) => {
|
||||
const { state, props } = this;
|
||||
const { value, mode } = state;
|
||||
const localeCode = getLocaleCode(context);
|
||||
if (value && localeCode) {
|
||||
value.locale(localeCode);
|
||||
}
|
||||
const { prefixCls, style, className, fullscreen, dateFullCellRender, monthFullCellRender } = props;
|
||||
const type = (mode === 'year') ? 'month' : 'date';
|
||||
const locale = getComponentLocale(props, context, 'Calendar', () => require('./locale/zh_CN'));
|
||||
|
||||
let cls = className || '';
|
||||
if (fullscreen) {
|
||||
@ -217,4 +210,15 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LocaleReceiver
|
||||
componentName="Calendar"
|
||||
defaultLocale={this.getDefaultLocale}
|
||||
>
|
||||
{this.renderCalendar}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,10 @@ title: Calendar
|
||||
**注意:**Calendar 部分 locale 是从 value 中读取,所以请先正确设置 moment 的 locale。
|
||||
|
||||
```jsx
|
||||
import moment from 'moment';
|
||||
|
||||
// 推荐在入口文件全局设置 locale
|
||||
import 'moment/locale/zh-cn';
|
||||
moment.locale('zh-cn');
|
||||
// 默认语言为 en-US,所以如果需要使用其他语言,推荐在入口文件全局设置 locale
|
||||
// import moment from 'moment';
|
||||
// import 'moment/locale/zh-cn';
|
||||
// moment.locale('zh-cn');
|
||||
|
||||
<Calendar
|
||||
dateCellRender={dateCellRender}
|
||||
|
2
components/calendar/locale/ar_EG.tsx
Normal file
2
components/calendar/locale/ar_EG.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import ar_EG from '../../date-picker/locale/ar_EG';
|
||||
export default ar_EG;
|
2
components/calendar/locale/is_IS.tsx
Normal file
2
components/calendar/locale/is_IS.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import is_IS from '../../date-picker/locale/is_IS';
|
||||
export default is_IS;
|
2
components/calendar/locale/uk_UA.tsx
Normal file
2
components/calendar/locale/uk_UA.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import uk_UA from '../../date-picker/locale/uk_UA';
|
||||
export default uk_UA;
|
@ -1,10 +1,10 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@full-calendar-prefix-cls: ~"@{ant-prefix}-fullcalendar";
|
||||
|
||||
.@{full-calendar-prefix-cls} {
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
.reset-component;
|
||||
outline: none;
|
||||
border-top: @border-width-base @border-style-base @border-color-base;
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
}
|
||||
|
||||
&-calendar-body {
|
||||
padding: 8px 8px 14px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
table {
|
||||
@ -46,7 +46,7 @@
|
||||
max-width: 100%;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
height: 246px;
|
||||
height: 256px;
|
||||
}
|
||||
|
||||
table,
|
||||
@ -91,12 +91,12 @@
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
color: @text-color;
|
||||
border-radius: @border-radius-base;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: @border-radius-sm;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
line-height: 22px;
|
||||
line-height: 24px;
|
||||
transition: all .3s;
|
||||
|
||||
&:hover {
|
||||
@ -116,7 +116,7 @@
|
||||
|
||||
&-today &-value,
|
||||
&-month-panel-current-cell &-value {
|
||||
box-shadow: 0 0 0 1px @primary-color;
|
||||
box-shadow: 0 0 0 1px @primary-color inset;
|
||||
}
|
||||
|
||||
&-selected-day &-value,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export interface CardGridProps {
|
||||
|
33
components/card/Meta.tsx
Normal file
33
components/card/Meta.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export interface CardMetaProps {
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
avatar?: React.ReactNode;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default (props: CardMetaProps) => {
|
||||
const { prefixCls = 'ant-card', className, avatar, title, description, ...others } = props;
|
||||
const classString = classNames(`${prefixCls}-meta`, className);
|
||||
const avatarDom = avatar ? <div className={`${prefixCls}-meta-avatar`}>{avatar}</div> : null;
|
||||
const titleDom = title ? <div className={`${prefixCls}-meta-title`}>{title}</div> : null;
|
||||
const descriptionDom = description ?
|
||||
<div className={`${prefixCls}-meta-description`}>{description}</div> : null;
|
||||
const MetaDetail = titleDom || descriptionDom ?
|
||||
<div className={`${prefixCls}-meta-detail`}>
|
||||
{titleDom}
|
||||
{descriptionDom}
|
||||
</div> : null;
|
||||
return (
|
||||
<div {...others} className={classString}>
|
||||
<div className={`${prefixCls}-meta-content`}>
|
||||
{avatarDom}
|
||||
{MetaDetail}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ describe('Card', () => {
|
||||
});
|
||||
|
||||
function fakeResizeWindowTo(wrapper, width) {
|
||||
Object.defineProperties(wrapper.node.container, {
|
||||
Object.defineProperties(wrapper.instance().container, {
|
||||
offsetWidth: {
|
||||
get() { return width; },
|
||||
configurable: true,
|
||||
@ -27,9 +27,11 @@ describe('Card', () => {
|
||||
const wrapper = mount(<Card title="xxx">xxx</Card>);
|
||||
fakeResizeWindowTo(wrapper, 1000);
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.hasClass('ant-card-wider-padding')).toBe(true);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-card-wider-padding').length).toBe(1);
|
||||
fakeResizeWindowTo(wrapper, 800);
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.hasClass('ant-card-wider-padding')).toBe(false);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-card-wider-padding').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -24,3 +24,9 @@ ReactDOM.render(
|
||||
</Card>
|
||||
, mountNode);
|
||||
````
|
||||
|
||||
<style>
|
||||
.code-box-demo p {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
33
components/card/demo/flexible-content.md
Normal file
33
components/card/demo/flexible-content.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 更灵活的内容展示
|
||||
en-US: Customized content
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以利用 `Card.Meta` 支持更灵活的内容。
|
||||
|
||||
## en-US
|
||||
|
||||
You can use `Card.Meta` to support more flexible content.
|
||||
|
||||
|
||||
````jsx
|
||||
import { Card } from 'antd';
|
||||
const { Meta } = Card;
|
||||
|
||||
ReactDOM.render(
|
||||
<Card
|
||||
hoverable
|
||||
style={{ width: 240 }}
|
||||
cover={<img alt="example" src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png" />}
|
||||
>
|
||||
<Meta
|
||||
title="Europe Street beat"
|
||||
description="www.instagram.com"
|
||||
/>
|
||||
</Card>
|
||||
, mountNode);
|
||||
````
|
@ -22,7 +22,7 @@ const gridStyle = {
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<Card title="Card Title" noHovering>
|
||||
<Card title="Card Title">
|
||||
<Card.Grid style={gridStyle}>Content</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>Content</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>Content</Card.Grid>
|
||||
|
48
components/card/demo/inner.md
Normal file
48
components/card/demo/inner.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
order: 7
|
||||
title:
|
||||
zh-CN: 内部卡片
|
||||
en-US: Inner card
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以放在普通卡片内部,展示多层级结构的信息。
|
||||
|
||||
## en-US
|
||||
|
||||
It can be placed inside the ordinary card to display the information of the multilevel structure.
|
||||
|
||||
````jsx
|
||||
import { Card } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<Card title="Card title">
|
||||
<p
|
||||
style={{
|
||||
fontSize: 14,
|
||||
color: 'rgba(0, 0, 0, 0.85)',
|
||||
marginBottom: 16,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Group title
|
||||
</p>
|
||||
<Card
|
||||
type="inner"
|
||||
title="Inner Card title"
|
||||
extra={<a href="#">More</a>}
|
||||
>
|
||||
Inner Card content
|
||||
</Card>
|
||||
<Card
|
||||
style={{ marginTop: 16 }}
|
||||
type="inner"
|
||||
title="Inner Card title"
|
||||
extra={<a href="#">More</a>}
|
||||
>
|
||||
Inner Card content
|
||||
</Card>
|
||||
</Card>
|
||||
, mountNode);
|
||||
````
|
33
components/card/demo/meta.md
Normal file
33
components/card/demo/meta.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
order: 9
|
||||
title:
|
||||
zh-CN: 支持更多内容配置
|
||||
en-US: Support more content configuration
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
一种支持封面、头像、标题和描述信息的卡片。
|
||||
|
||||
## en-US
|
||||
|
||||
A Card that supports `cover`, `avatar`, `title` and `description`.
|
||||
|
||||
````jsx
|
||||
import { Card, Icon, Avatar } from 'antd';
|
||||
const { Meta } = Card;
|
||||
|
||||
ReactDOM.render(
|
||||
<Card
|
||||
style={{ width: 300 }}
|
||||
cover={<img alt="example" src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png" />}
|
||||
actions={[<Icon type="setting" />, <Icon type="edit" />, <Icon type="ellipsis" />]}
|
||||
>
|
||||
<Meta
|
||||
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
|
||||
title="Card title"
|
||||
description="This is the description"
|
||||
/>
|
||||
</Card>
|
||||
, mountNode);
|
||||
````
|
@ -1,43 +0,0 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 更灵活的内容展示
|
||||
en-US: Customized content
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以调整默认边距,设定宽度。
|
||||
|
||||
## en-US
|
||||
|
||||
Customizing default width and margin.
|
||||
|
||||
|
||||
````jsx
|
||||
import { Card } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<Card style={{ width: 240 }} bodyStyle={{ padding: 0 }}>
|
||||
<div className="custom-image">
|
||||
<img alt="example" width="100%" src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png" />
|
||||
</div>
|
||||
<div className="custom-card">
|
||||
<h3>Europe Street beat</h3>
|
||||
<p>www.instagram.com</p>
|
||||
</div>
|
||||
</Card>
|
||||
, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
.custom-image img {
|
||||
display: block;
|
||||
}
|
||||
.custom-card {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
.custom-card p {
|
||||
color: #999;
|
||||
}
|
||||
````
|
86
components/card/demo/tabs.md
Normal file
86
components/card/demo/tabs.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
order: 8
|
||||
title:
|
||||
zh-CN: 带页签的卡片
|
||||
en-US: With tabs
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可承载更多内容。
|
||||
|
||||
## en-US
|
||||
|
||||
More content can be hosted.
|
||||
|
||||
````jsx
|
||||
import { Card } from 'antd';
|
||||
|
||||
const tabList = [{
|
||||
key: 'tab1',
|
||||
tab: 'tab1',
|
||||
}, {
|
||||
key: 'tab2',
|
||||
tab: 'tab2',
|
||||
}];
|
||||
|
||||
const contentList = {
|
||||
tab1: <p>content1</p>,
|
||||
tab2: <p>content2</p>,
|
||||
};
|
||||
|
||||
const tabListNoTitle = [{
|
||||
key: 'article',
|
||||
tab: 'article',
|
||||
}, {
|
||||
key: 'app',
|
||||
tab: 'app',
|
||||
}, {
|
||||
key: 'project',
|
||||
tab: 'project',
|
||||
}];
|
||||
|
||||
const contentListNoTitle = {
|
||||
article: <p>article content</p>,
|
||||
app: <p>app content</p>,
|
||||
project: <p>project content</p>,
|
||||
};
|
||||
|
||||
class TabsCard extends React.Component {
|
||||
state = {
|
||||
key: 'tab1',
|
||||
noTitleKey: 'article',
|
||||
}
|
||||
onTabChange = (key, type) => {
|
||||
console.log(key, type);
|
||||
this.setState({ [type]: key });
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Card
|
||||
style={{ width: '100%' }}
|
||||
title="Card title"
|
||||
extra={<a href="#">More</a>}
|
||||
tabList={tabList}
|
||||
onTabChange={(key) => { this.onTabChange(key, 'key'); }}
|
||||
>
|
||||
{contentList[this.state.key]}
|
||||
</Card>
|
||||
<br /><br />
|
||||
<Card
|
||||
style={{ width: '100%' }}
|
||||
tabList={tabListNoTitle}
|
||||
onTabChange={(key) => { this.onTabChange(key, 'noTitleKey'); }}
|
||||
>
|
||||
{contentListNoTitle[this.state.noTitleKey]}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<TabsCard />
|
||||
, mountNode);
|
||||
````
|
@ -21,12 +21,17 @@ A card can be used to display content related to a single subject. The content c
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| actions | The action list, shows at the bottom of the Card. | Array<ReactNode> | - |
|
||||
| bodyStyle | Inline style to apply to the card content | object | - |
|
||||
| bordered | Toggles rendering of the border around the card | boolean | `true` |
|
||||
| cover | Card cover | ReactNode | - |
|
||||
| extra | Content to render in the top-right corner of the card | string\|ReactNode | - |
|
||||
| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | `false` |
|
||||
| noHovering | Whether to disable hover effect on mouse over | boolean | `false` |
|
||||
| hoverable | Lift up when hovering card | boolean | false |
|
||||
| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false |
|
||||
| tabList | List of TabPane's head. | Array<{key: string, tab: ReactNode}> | - |
|
||||
| title | Card title | string\|ReactNode | - |
|
||||
| type | Card style type, can be set to `inner` or not set | string | - |
|
||||
| onTabChange | Callback when tab is switched | (key) => void | - |
|
||||
|
||||
### Card.Grid
|
||||
|
||||
@ -34,3 +39,13 @@ A card can be used to display content related to a single subject. The content c
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| className | className of container | string | - |
|
||||
| style | style object of container | object | - |
|
||||
|
||||
### Card.Meta
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| avatar | avatar or icon | ReactNode | - |
|
||||
| className | className of container | string | - |
|
||||
| description | description content | ReactNode | - |
|
||||
| style | style object of container | object | - |
|
||||
| title | title content | ReactNode | - |
|
||||
|
@ -1,8 +1,22 @@
|
||||
import React, { Component, Children } from 'react';
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import omit from 'omit.js';
|
||||
import Grid from './Grid';
|
||||
import Meta from './Meta';
|
||||
import Tabs from '../tabs';
|
||||
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
export { CardGridProps } from './Grid';
|
||||
export { CardMetaProps } from './Meta';
|
||||
|
||||
export type CardType = 'inner';
|
||||
|
||||
export interface CardTabListType {
|
||||
key: string;
|
||||
tab: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface CardProps {
|
||||
prefixCls?: string;
|
||||
@ -13,22 +27,37 @@ export interface CardProps {
|
||||
style?: React.CSSProperties;
|
||||
loading?: boolean;
|
||||
noHovering?: boolean;
|
||||
hoverable?: boolean;
|
||||
children?: React.ReactNode;
|
||||
id?: string;
|
||||
className?: string;
|
||||
type?: CardType;
|
||||
cover?: React.ReactNode;
|
||||
actions?: Array<React.ReactNode>;
|
||||
tabList?: CardTabListType[];
|
||||
onTabChange?: (key: string) => void;
|
||||
}
|
||||
|
||||
export default class Card extends Component<CardProps, {}> {
|
||||
export default class Card extends React.Component<CardProps, {}> {
|
||||
static Grid: typeof Grid = Grid;
|
||||
container: HTMLDivElement;
|
||||
static Meta: typeof Meta = Meta;
|
||||
resizeEvent: any;
|
||||
updateWiderPaddingCalled: boolean;
|
||||
state = {
|
||||
widerPadding: false,
|
||||
};
|
||||
private container: HTMLDivElement;
|
||||
componentDidMount() {
|
||||
this.updateWiderPadding();
|
||||
this.resizeEvent = addEventListener(window, 'resize', this.updateWiderPadding);
|
||||
|
||||
if ('noHovering' in this.props) {
|
||||
warning(
|
||||
!this.props.noHovering,
|
||||
'`noHovering` of Card is deperated, you can remove it safely or use `hoverable` instead.',
|
||||
);
|
||||
warning(!!this.props.noHovering, '`noHovering={false}` of Card is deperated, use `hoverable` instead.');
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (this.resizeEvent) {
|
||||
@ -54,73 +83,122 @@ export default class Card extends Component<CardProps, {}> {
|
||||
});
|
||||
}
|
||||
}
|
||||
onTabChange = (key: string) => {
|
||||
if (this.props.onTabChange) {
|
||||
this.props.onTabChange(key);
|
||||
}
|
||||
}
|
||||
saveRef = (node: HTMLDivElement) => {
|
||||
this.container = node;
|
||||
}
|
||||
isContainGrid() {
|
||||
let containGrid;
|
||||
Children.forEach(this.props.children, (element: JSX.Element) => {
|
||||
React.Children.forEach(this.props.children, (element: JSX.Element) => {
|
||||
if (element && element.type && element.type === Grid) {
|
||||
containGrid = true;
|
||||
}
|
||||
});
|
||||
return containGrid;
|
||||
}
|
||||
getAction(actions: React.ReactNode[]) {
|
||||
if (!actions || !actions.length) {
|
||||
return null;
|
||||
}
|
||||
const actionList = actions.map((action, index) => (
|
||||
<li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}>
|
||||
<span>{action}</span>
|
||||
</li>
|
||||
),
|
||||
);
|
||||
return actionList;
|
||||
}
|
||||
// For 2.x compatible
|
||||
getCompatibleHoverable() {
|
||||
const { noHovering, hoverable } = this.props;
|
||||
if ('noHovering' in this.props) {
|
||||
return !noHovering || hoverable;
|
||||
}
|
||||
return !!hoverable;
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
prefixCls = 'ant-card', className, extra, bodyStyle, noHovering,
|
||||
title, loading, bordered = true, ...others,
|
||||
prefixCls = 'ant-card', className, extra, bodyStyle, noHovering, hoverable, title, loading,
|
||||
bordered = true, type, cover, actions, tabList, children, ...others,
|
||||
} = this.props;
|
||||
let children = this.props.children;
|
||||
|
||||
const classString = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-loading`]: loading,
|
||||
[`${prefixCls}-bordered`]: bordered,
|
||||
[`${prefixCls}-no-hovering`]: noHovering,
|
||||
[`${prefixCls}-hoverable`]: this.getCompatibleHoverable(),
|
||||
[`${prefixCls}-wider-padding`]: this.state.widerPadding,
|
||||
[`${prefixCls}-padding-transition`]: this.updateWiderPaddingCalled,
|
||||
[`${prefixCls}-contain-grid`]: this.isContainGrid(),
|
||||
[`${prefixCls}-contain-tabs`]: tabList && tabList.length,
|
||||
[`${prefixCls}-type-${type}`]: !!type,
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
children = (
|
||||
<div className={`${prefixCls}-loading-content`}>
|
||||
<p className={`${prefixCls}-loading-block`} style={{ width: '94%' }} />
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '28%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '62%' }} />
|
||||
</p>
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '22%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '66%' }} />
|
||||
</p>
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '56%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '39%' }} />
|
||||
</p>
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '21%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '15%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '40%' }} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const loadingBlock = (
|
||||
<div className={`${prefixCls}-loading-content`}>
|
||||
<p className={`${prefixCls}-loading-block`} style={{ width: '94%' }} />
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '28%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '62%' }} />
|
||||
</p>
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '22%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '66%' }} />
|
||||
</p>
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '56%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '39%' }} />
|
||||
</p>
|
||||
<p>
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '21%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '15%' }} />
|
||||
<span className={`${prefixCls}-loading-block`} style={{ width: '40%' }} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
let head;
|
||||
if (title || extra) {
|
||||
const tabs = tabList && tabList.length ? (
|
||||
<Tabs className={`${prefixCls}-head-tabs`} size="large" onChange={this.onTabChange}>
|
||||
{tabList.map(item => <Tabs.TabPane tab={item.tab} key={item.key} />)}
|
||||
</Tabs>
|
||||
) : null;
|
||||
if (title || extra || tabs) {
|
||||
head = (
|
||||
<div className={`${prefixCls}-head`}>
|
||||
{title ? <div className={`${prefixCls}-head-title`}>{title}</div> : null}
|
||||
{extra ? <div className={`${prefixCls}-extra`}>{extra}</div> : null}
|
||||
<div className={`${prefixCls}-head-wrapper`}>
|
||||
{title && <div className={`${prefixCls}-head-title`}>{title}</div>}
|
||||
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
|
||||
</div>
|
||||
{tabs}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...others} className={classString} ref={this.saveRef}>
|
||||
const coverDom = cover ? <div className={`${prefixCls}-cover`}>{cover}</div> : null;
|
||||
const body = (
|
||||
<div className={`${prefixCls}-body`} style={bodyStyle}>
|
||||
{loading ? loadingBlock : <div>{children}</div>}
|
||||
</div>
|
||||
);
|
||||
const mainContent = (
|
||||
<div>
|
||||
{head}
|
||||
<div className={`${prefixCls}-body`} style={bodyStyle}>{children}</div>
|
||||
{coverDom}
|
||||
{children ? body : null}
|
||||
</div>
|
||||
);
|
||||
const actionDom = actions && actions.length ?
|
||||
<ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul> : null;
|
||||
const divProps = omit(others, [
|
||||
'onTabChange',
|
||||
]);
|
||||
return (
|
||||
<div {...divProps} className={classString} ref={this.saveRef}>
|
||||
{mainContent}
|
||||
{actionDom}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -22,12 +22,17 @@ cols: 1
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| actions | 卡片操作组,位置在卡片底部 | Array<ReactNode> | - |
|
||||
| bodyStyle | 内容区域自定义样式 | object | - |
|
||||
| bordered | 是否有边框 | boolean | true |
|
||||
| cover | 卡片封面 | ReactNode | - |
|
||||
| extra | 卡片右上角的操作区域 | string\|ReactNode | - |
|
||||
| hoverable | 鼠标移过时可浮起 | boolean | false |
|
||||
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false |
|
||||
| noHovering | 取消鼠标移过浮起 | boolean | false |
|
||||
| tabList | 页签标题列表 | Array<{key: string, tab: ReactNode}> | - |
|
||||
| title | 卡片标题 | string\|ReactNode | - |
|
||||
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - |
|
||||
| onTabChange | 页签切换的回调 | (key) => void | - |
|
||||
|
||||
### Card.Grid
|
||||
|
||||
@ -35,3 +40,13 @@ cols: 1
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| className | 网格容器类名 | string | - |
|
||||
| style | 定义网格容器类名的样式 | object | - |
|
||||
|
||||
### Card.Meta
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| avatar | 头像/图标 | ReactNode | - |
|
||||
| className | 容器类名 | string | - |
|
||||
| description | 描述内容 | ReactNode | - |
|
||||
| style | 定义容器类名的样式 | object | - |
|
||||
| title | 标题内容 | ReactNode | - |
|
||||
|
@ -2,19 +2,21 @@
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@card-prefix-cls: ~"@{ant-prefix}-card";
|
||||
@card-padding-base: 24px;
|
||||
@card-padding-wider: 32px;
|
||||
@card-head-height: 48px;
|
||||
|
||||
.@{card-prefix-cls} {
|
||||
.reset-component;
|
||||
background: @component-background;
|
||||
border-radius: @border-radius-sm;
|
||||
font-size: @font-size-base;
|
||||
position: relative;
|
||||
transition: all .3s;
|
||||
|
||||
&:not(&-no-hovering):hover {
|
||||
box-shadow: @box-shadow-base;
|
||||
border-color: @shadow-color;
|
||||
&-hoverable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: @card-shadow;
|
||||
border-color: rgba(0, 0, 0, 0.09);
|
||||
}
|
||||
}
|
||||
|
||||
&-bordered {
|
||||
@ -22,20 +24,22 @@
|
||||
}
|
||||
|
||||
&-head {
|
||||
height: @card-head-height;
|
||||
line-height: @card-head-height;
|
||||
background: @card-head-background;
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
padding: 0 @card-padding-base;
|
||||
border-radius: @border-radius-sm @border-radius-sm 0 0;
|
||||
.clearfix;
|
||||
margin-bottom: -1px; // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png
|
||||
display: flex;
|
||||
min-height: @card-head-height;
|
||||
|
||||
&-wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&-title {
|
||||
font-size: @font-size-lg;
|
||||
padding: @card-head-padding 0;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: @card-head-color;
|
||||
@ -43,10 +47,20 @@
|
||||
display: inline-block;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-tabs {
|
||||
margin-bottom: -17px;
|
||||
clear: both;
|
||||
|
||||
&-bar {
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-extra {
|
||||
float: right;
|
||||
padding: @card-head-padding + 1.5px 0;
|
||||
text-align: right;
|
||||
// https://stackoverflow.com/a/22429853/3040605
|
||||
margin-left: auto;
|
||||
@ -77,6 +91,66 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-contain-tabs &-head-title {
|
||||
padding-bottom: 0;
|
||||
min-height: @card-head-height - @card-head-padding;
|
||||
}
|
||||
|
||||
&-contain-tabs &-extra {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&-cover > * {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
background: @card-actions-background;
|
||||
.clearfix;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
& > li {
|
||||
float: left;
|
||||
text-align: center;
|
||||
margin: 12px 0;
|
||||
color: @text-color-secondary;
|
||||
|
||||
& > span {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
line-height: 22px;
|
||||
min-width: 32px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
transition: color .3s;
|
||||
}
|
||||
|
||||
& > .anticon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @text-color-secondary;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-wider-padding &-head {
|
||||
padding: 0 @card-padding-wider;
|
||||
}
|
||||
@ -85,10 +159,6 @@
|
||||
padding: @card-padding-base @card-padding-wider;
|
||||
}
|
||||
|
||||
&-wider-padding &-extra {
|
||||
right: @card-padding-wider;
|
||||
}
|
||||
|
||||
&-padding-transition &-head,
|
||||
&-padding-transition &-body {
|
||||
transition: padding .3s;
|
||||
@ -98,6 +168,61 @@
|
||||
transition: right .3s;
|
||||
}
|
||||
|
||||
&-type-inner &-head {
|
||||
padding: 0 @card-padding-base;
|
||||
background: @background-color-light;
|
||||
|
||||
&-title {
|
||||
padding: @card-inner-head-padding 0;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
|
||||
&-type-inner &-body {
|
||||
padding: 16px @card-padding-base;
|
||||
}
|
||||
|
||||
&-type-inner &-extra {
|
||||
padding: @card-inner-head-padding + 1.5px 0;
|
||||
}
|
||||
|
||||
&-meta {
|
||||
margin: -4px 0;
|
||||
|
||||
&-content {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
padding-right: 16px;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
&-detail {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
|
||||
& > div:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
font-size: @font-size-lg;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: @card-head-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&-description {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
&-loading &-body {
|
||||
user-select: none;
|
||||
padding: 0;
|
||||
@ -105,11 +230,14 @@
|
||||
|
||||
&-loading-content {
|
||||
padding: @card-padding-base;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-loading-block {
|
||||
display: inline-block;
|
||||
margin: 5px 1% 0;
|
||||
margin: 5px 2% 0 0;
|
||||
height: 14px;
|
||||
border-radius: @border-radius-sm;
|
||||
background: linear-gradient(90deg, rgba(207, 216, 220, .2), rgba(207, 216, 220, .4), rgba(207, 216, 220, .2));
|
||||
|
@ -1,2 +1,5 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../tabs/style';
|
||||
|
@ -24,7 +24,7 @@ exports[`renders ./components/carousel/demo/autoplay.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-active slick-cloned"
|
||||
data-index="0"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -34,7 +34,7 @@ exports[`renders ./components/carousel/demo/autoplay.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="1"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -44,7 +44,7 @@ exports[`renders ./components/carousel/demo/autoplay.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="2"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -54,7 +54,7 @@ exports[`renders ./components/carousel/demo/autoplay.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="3"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -99,7 +99,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-active slick-cloned"
|
||||
data-index="0"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -109,7 +109,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="1"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -119,7 +119,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="2"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -129,7 +129,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="3"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -166,7 +166,7 @@ exports[`renders ./components/carousel/demo/fade.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-active slick-cloned"
|
||||
data-index="0"
|
||||
style="outline:none;position:relative;left:0;opacity:1;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease;"
|
||||
style="outline:none;position:relative;left:0;opacity:1;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -176,7 +176,7 @@ exports[`renders ./components/carousel/demo/fade.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="1"
|
||||
style="outline:none;position:relative;left:0;opacity:0;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease;"
|
||||
style="outline:none;position:relative;left:0;opacity:0;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -186,7 +186,7 @@ exports[`renders ./components/carousel/demo/fade.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="2"
|
||||
style="outline:none;position:relative;left:0;opacity:0;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease;"
|
||||
style="outline:none;position:relative;left:0;opacity:0;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -196,7 +196,7 @@ exports[`renders ./components/carousel/demo/fade.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="3"
|
||||
style="outline:none;position:relative;left:0;opacity:0;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease;"
|
||||
style="outline:none;position:relative;left:0;opacity:0;transition:opacity 500ms ease;-webkit-transition:opacity 500ms ease"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -233,7 +233,7 @@ exports[`renders ./components/carousel/demo/vertical.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-active slick-cloned"
|
||||
data-index="0"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -243,7 +243,7 @@ exports[`renders ./components/carousel/demo/vertical.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="1"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -253,7 +253,7 @@ exports[`renders ./components/carousel/demo/vertical.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="2"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
@ -263,7 +263,7 @@ exports[`renders ./components/carousel/demo/vertical.md correctly 1`] = `
|
||||
<div
|
||||
class="slick-slide slick-cloned"
|
||||
data-index="3"
|
||||
style="outline:none;"
|
||||
style="outline:none"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3>
|
||||
|
@ -5,8 +5,8 @@ import Carousel from '..';
|
||||
describe('Carousel', () => {
|
||||
it('should has innerSlider', () => {
|
||||
const wrapper = mount(<Carousel><div /></Carousel>);
|
||||
const { innerSlider } = wrapper.node;
|
||||
const innerSliderFromRefs = wrapper.node.refs.slick.innerSlider;
|
||||
const { innerSlider } = wrapper.instance();
|
||||
const innerSliderFromRefs = wrapper.instance().slick.innerSlider;
|
||||
expect(innerSlider).toBe(innerSliderFromRefs);
|
||||
expect(typeof innerSlider.slickNext).toBe('function');
|
||||
});
|
||||
|
@ -24,4 +24,12 @@ A carousel component. Scales with its container.
|
||||
| effect | Transition effect | `scrollx` \| `fade` | `scrollx` |
|
||||
| vertical | Whether to use a vertical display | boolean | `false` |
|
||||
|
||||
## Methods
|
||||
|
||||
| Name | Description |
|
||||
| ---- | ----------- |
|
||||
| goTo(slideNumber) | Change current slide to given slide number |
|
||||
| next() | Change current slide to next slide |
|
||||
| prev() | Change current slide to previous slide |
|
||||
|
||||
For more info on the parameters, refer to the <https://github.com/akiran/react-slick>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
// matchMedia polyfill for
|
||||
@ -66,7 +66,7 @@ export interface CarouselProps {
|
||||
slickGoTo?: number;
|
||||
}
|
||||
|
||||
export default class Carousel extends React.Component<CarouselProps, any> {
|
||||
export default class Carousel extends React.Component<CarouselProps, {}> {
|
||||
static defaultProps = {
|
||||
dots: true,
|
||||
arrows: false,
|
||||
@ -74,13 +74,11 @@ export default class Carousel extends React.Component<CarouselProps, any> {
|
||||
draggable: false,
|
||||
};
|
||||
|
||||
refs: {
|
||||
slick: any,
|
||||
};
|
||||
|
||||
innerSlider: any;
|
||||
|
||||
constructor(props) {
|
||||
private slick: any;
|
||||
|
||||
constructor(props: CarouselProps) {
|
||||
super(props);
|
||||
this.onWindowResized = debounce(this.onWindowResized, 500, {
|
||||
leading: false,
|
||||
@ -92,9 +90,8 @@ export default class Carousel extends React.Component<CarouselProps, any> {
|
||||
if (autoplay) {
|
||||
window.addEventListener('resize', this.onWindowResized);
|
||||
}
|
||||
const { slick } = this.refs;
|
||||
// https://github.com/ant-design/ant-design/issues/7191
|
||||
this.innerSlider = slick && slick.innerSlider;
|
||||
this.innerSlider = this.slick && this.slick.innerSlider;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -107,13 +104,28 @@ export default class Carousel extends React.Component<CarouselProps, any> {
|
||||
|
||||
onWindowResized = () => {
|
||||
// Fix https://github.com/ant-design/ant-design/issues/2550
|
||||
const { slick } = this.refs;
|
||||
const { autoplay } = this.props;
|
||||
if (autoplay && slick && slick.innerSlider && slick.innerSlider.autoPlay) {
|
||||
slick.innerSlider.autoPlay();
|
||||
if (autoplay && this.slick && this.slick.innerSlider && this.slick.innerSlider.autoPlay) {
|
||||
this.slick.innerSlider.autoPlay();
|
||||
}
|
||||
}
|
||||
|
||||
saveSlick = (node: any) => {
|
||||
this.slick = node;
|
||||
}
|
||||
|
||||
next() {
|
||||
this.slick.slickNext();
|
||||
}
|
||||
|
||||
prev() {
|
||||
this.slick.slickPrev();
|
||||
}
|
||||
|
||||
goTo(slide: number) {
|
||||
this.slick.slickGoTo(slide);
|
||||
}
|
||||
|
||||
render() {
|
||||
let props = {
|
||||
...this.props,
|
||||
@ -130,7 +142,7 @@ export default class Carousel extends React.Component<CarouselProps, any> {
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<SlickCarousel ref="slick" {...props} />
|
||||
<SlickCarousel ref={this.saveSlick} {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -25,4 +25,12 @@ subtitle: 走马灯
|
||||
| effect | 动画效果函数,可取 scrollx, fade | string | scrollx |
|
||||
| vertical | 垂直显示 | boolean | false |
|
||||
|
||||
## 方法
|
||||
|
||||
| 名称 | 描述 |
|
||||
| --- | --- |
|
||||
| goTo(slideNumber) | 切换到指定面板 |
|
||||
| next() | 切换到下一面板 |
|
||||
| prev() | 切换到上一面板 |
|
||||
|
||||
更多参数可参考:<https://github.com/akiran/react-slick>
|
||||
|
@ -2,6 +2,8 @@
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
.@{ant-prefix}-carousel {
|
||||
.reset-component;
|
||||
|
||||
.slick-slider {
|
||||
position: relative;
|
||||
display: block;
|
||||
@ -141,6 +143,7 @@
|
||||
list-style: none;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: @carousel-dot-height;
|
||||
@ -164,6 +167,7 @@
|
||||
font-size: 0;
|
||||
color: transparent;
|
||||
transition: all .5s;
|
||||
padding: 0;
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 0.75;
|
||||
|
@ -47,7 +47,7 @@ exports[`renders ./components/cascader/demo/change-on-select.md correctly 1`] =
|
||||
exports[`renders ./components/cascader/demo/custom-render.md correctly 1`] = `
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
style="width:270px;"
|
||||
style="width:100%"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
@ -211,7 +211,7 @@ exports[`renders ./components/cascader/demo/search.md correctly 1`] = `
|
||||
exports[`renders ./components/cascader/demo/size.md correctly 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
class="ant-cascader-picker ant-cascader-picker-large"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
@ -253,7 +253,7 @@ exports[`renders ./components/cascader/demo/size.md correctly 1`] = `
|
||||
<br />
|
||||
<br />
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
class="ant-cascader-picker ant-cascader-picker-small"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { render, mount } from 'enzyme';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import Cascader from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
|
||||
const options = [{
|
||||
value: 'zhejiang',
|
||||
@ -28,11 +29,13 @@ const options = [{
|
||||
}];
|
||||
|
||||
describe('Cascader', () => {
|
||||
focusTest(Cascader);
|
||||
|
||||
it('popup correctly when panel is hidden', () => {
|
||||
const wrapper = mount(
|
||||
<Cascader options={options} />
|
||||
);
|
||||
expect(render(wrapper.find('Trigger').node.getComponent())).toMatchSnapshot();
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('popup correctly when panel is open', () => {
|
||||
@ -40,7 +43,7 @@ describe('Cascader', () => {
|
||||
<Cascader options={options} />
|
||||
);
|
||||
wrapper.find('input').simulate('click');
|
||||
expect(render(wrapper.find('Trigger').node.getComponent())).toMatchSnapshot();
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('popup correctly with defaultValue', () => {
|
||||
@ -48,24 +51,24 @@ describe('Cascader', () => {
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />
|
||||
);
|
||||
wrapper.find('input').simulate('click');
|
||||
expect(render(wrapper.find('Trigger').node.getComponent())).toMatchSnapshot();
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('can be selected', () => {
|
||||
const wrapper = mount(<Cascader options={options} />);
|
||||
wrapper.find('input').simulate('click');
|
||||
let popupWrapper = mount(wrapper.find('Trigger').node.getComponent());
|
||||
let popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||
popupWrapper.find('.ant-cascader-menu').at(0).find('.ant-cascader-menu-item').at(0)
|
||||
.simulate('click');
|
||||
expect(render(wrapper.find('Trigger').node.getComponent())).toMatchSnapshot();
|
||||
popupWrapper = mount(wrapper.find('Trigger').node.getComponent());
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||
popupWrapper.find('.ant-cascader-menu').at(1).find('.ant-cascader-menu-item').at(0)
|
||||
.simulate('click');
|
||||
expect(render(wrapper.find('Trigger').node.getComponent())).toMatchSnapshot();
|
||||
popupWrapper = mount(wrapper.find('Trigger').node.getComponent());
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||
popupWrapper.find('.ant-cascader-menu').at(2).find('.ant-cascader-menu-item').at(0)
|
||||
.simulate('click');
|
||||
expect(render(wrapper.find('Trigger').node.getComponent())).toMatchSnapshot();
|
||||
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('backspace should work with `Cascader[showSearch]`', () => {
|
||||
|
@ -64,7 +64,7 @@ ReactDOM.render(
|
||||
options={options}
|
||||
defaultValue={['zhejiang', 'hangzhou', 'xihu']}
|
||||
displayRender={displayRender}
|
||||
style={{ width: 270 }}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
, mountNode);
|
||||
````
|
||||
|
@ -23,7 +23,7 @@ Cascade selection box.
|
||||
| allowClear | whether allow clear | boolean | true |
|
||||
| changeOnSelect | change value on each selection if set to true, see above demo for details | boolean | false |
|
||||
| className | additional css class | string | - |
|
||||
| defaultValue | initial selected value | [CascaderOptionType](https://git.io/vMMoK)\[] | \[] |
|
||||
| defaultValue | initial selected value | string\[] | \[] |
|
||||
| disabled | whether disabled select | boolean | false |
|
||||
| displayRender | render function of displaying selected options | `(label, selectedOptions) => ReactNode` | `label => label.join(' / ')` |
|
||||
| expandTrigger | expand current item when click or hover, one of 'click' 'hover' | string | 'click' |
|
||||
@ -37,7 +37,7 @@ Cascade selection box.
|
||||
| showSearch | Whether show search input in single mode. | boolean\|object | false |
|
||||
| size | input size, one of `large` `default` `small` | string | `default` |
|
||||
| style | additional style | string | - |
|
||||
| value | selected value | [CascaderOptionType](https://git.io/vMMoK)\[] | - |
|
||||
| value | selected value | string\[] | - |
|
||||
| onChange | callback when finishing cascader select | `(value, selectedOptions) => void` | - |
|
||||
| onPopupVisibleChange | callback when popup shown or hidden | `(value) => void` | - |
|
||||
| popupVisible | set visible of cascader popup | boolean | - |
|
||||
@ -53,6 +53,6 @@ Fields in `showSearch`:
|
||||
|
||||
<style>
|
||||
.ant-cascader-picker {
|
||||
width: 220px;
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user