Merge pull request #18051 from orzyyyy/perf

perf: migrate tslint to eslint
This commit is contained in:
偏右 2019-08-06 16:02:35 +08:00 committed by GitHub
commit 109bd4ecde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
153 changed files with 2276 additions and 2013 deletions

View File

@ -1,5 +1,12 @@
const eslintrc = {
extends: ['airbnb', 'prettier', 'plugin:jest/recommended'],
extends: [
'airbnb',
'prettier',
'plugin:jest/recommended',
'plugin:react/recommended',
'plugin:import/typescript',
'prettier/react',
],
env: {
browser: true,
node: true,
@ -7,8 +14,22 @@ const eslintrc = {
jest: true,
es6: true,
},
parser: 'babel-eslint',
plugins: ['markdown', 'react', 'babel', 'jest'],
settings: {
react: {
version: '16.8',
},
},
parser: '@typescript-eslint/parser',
plugins: ['markdown', 'react', 'babel', 'jest', '@typescript-eslint'],
// https://github.com/typescript-eslint/typescript-eslint/issues/46#issuecomment-470486034
overrides: [
{
files: ['*.tsx'],
rules: {
'@typescript-eslint/no-unused-vars': [2, { args: 'none' }],
},
},
],
rules: {
'react/jsx-one-expression-per-line': 0,
'react/prop-types': 0,
@ -29,12 +50,34 @@ const eslintrc = {
],
},
],
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.md'] }],
'jsx-a11y/no-static-element-interactions': 0,
'jsx-a11y/anchor-has-content': 0,
'jsx-a11y/click-events-have-key-events': 0,
'jsx-a11y/anchor-is-valid': 0,
'comma-dangle': ['error', 'always-multiline'],
'react/jsx-filename-extension': 0,
'prefer-destructuring': 0, // TODO: remove later
'consistent-return': 0, // TODO: remove later
'no-return-assign': 0, // TODO: remove later
'no-param-reassign': 0, // TODO: remove later
'react/destructuring-assignment': 0, // TODO: remove later
'react/no-did-update-set-state': 0, // TODO: remove later
'react/require-default-props': 0,
'react/default-props-match-prop-types': 0,
'import/no-cycle': 0,
'react/no-find-dom-node': 0,
'no-underscore-dangle': 0,
// label-has-for has been deprecated
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md
'jsx-a11y/label-has-for': 0,
// for (let i = 0; i < len; i++)
'no-plusplus': 0,
// https://eslint.org/docs/rules/no-continue
// labeledLoop is conflicted with `eslint . --fix`
'no-continue': 0,
'react/display-name': 0,
// ban this for Number.isNaN needs polyfill
'no-restricted-globals': 0,
},
};
@ -56,11 +99,6 @@ if (process.env.RUN_ENV === 'DEMO') {
'react/destructuring-assignment': 0,
'react/no-multi-comp': 0,
'jsx-a11y/href-no-hash': 0,
'prefer-destructuring': 0, // TODO: remove later
'max-len': 0, // TODO: remove later
'consistent-return': 0, // TODO: remove later
'no-return-assign': 0, // TODO: remove later
'no-param-reassign': 0, // TODO: remove later
'import/no-extraneous-dependencies': 0,
});
}

View File

@ -27,7 +27,7 @@ const getCollapsedHeight: MotionFunc = () => ({ height: 0, opacity: 0 });
const getRealHeight: MotionFunc = node => ({ height: node.scrollHeight, opacity: 1 });
const getCurrentHeight: MotionFunc = node => ({ height: node.offsetHeight });
export const collapseMotion: Motion = {
const collapseMotion: Motion = {
motionName: 'ant-motion-collapse',
onAppearStart: getCollapsedHeight,
onEnterStart: getCollapsedHeight,
@ -36,3 +36,5 @@ export const collapseMotion: Motion = {
onLeaveStart: getCurrentHeight,
onLeaveActive: getCollapsedHeight,
};
export default collapseMotion;

View File

@ -26,6 +26,7 @@ export function throttleByAnimationFrameDecorator() {
return {
configurable: true,
get() {
// eslint-disable-next-line no-prototype-builtins
if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) {
return fn;
}

View File

@ -20,6 +20,7 @@ const inlineStyle: React.CSSProperties = {
class TransButton extends React.Component<TransButtonProps> {
div?: HTMLDivElement;
lastKeyCode?: number;
onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = event => {

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import { findDOMNode } from 'react-dom';
import TransitionEvents from 'css-animation/lib/Event';
import raf from '../_util/raf';
import raf from './raf';
import { ConfigConsumer, ConfigConsumerProps, CSPConfig } from '../config-provider';
let styleForPesudo: HTMLStyleElement | null;
@ -14,24 +14,49 @@ function isHidden(element: HTMLElement) {
return !element || element.offsetParent === null;
}
function isNotGrey(color: string) {
// eslint-disable-next-line no-useless-escape
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
if (match && match[1] && match[2] && match[3]) {
return !(match[1] === match[2] && match[2] === match[3]);
}
return true;
}
export default class Wave extends React.Component<{ insertExtraNode?: boolean }> {
private instance?: {
cancel: () => void;
};
private extraNode: HTMLDivElement;
private clickWaveTimeoutId: number;
private animationStartId: number;
private animationStart: boolean = false;
private destroy: boolean = false;
private csp?: CSPConfig;
isNotGrey(color: string) {
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
if (match && match[1] && match[2] && match[3]) {
return !(match[1] === match[2] && match[2] === match[3]);
componentDidMount() {
const node = findDOMNode(this) as HTMLElement;
if (!node || node.nodeType !== 1) {
return;
}
return true;
this.instance = this.bindAnimationEvent(node);
}
componentWillUnmount() {
if (this.instance) {
this.instance.cancel();
}
if (this.clickWaveTimeoutId) {
clearTimeout(this.clickWaveTimeoutId);
}
this.destroy = true;
}
onClick = (node: HTMLElement, waveColor: string) => {
@ -40,7 +65,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
}
const { insertExtraNode } = this.props;
this.extraNode = document.createElement('div');
const extraNode = this.extraNode;
const { extraNode } = this;
extraNode.className = 'ant-click-animating-node';
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'true');
@ -50,7 +75,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
waveColor &&
waveColor !== '#ffffff' &&
waveColor !== 'rgb(255, 255, 255)' &&
this.isNotGrey(waveColor) &&
isNotGrey(waveColor) &&
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent'
) {
@ -75,6 +100,31 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
};
onTransitionStart = (e: AnimationEvent) => {
if (this.destroy) return;
const node = findDOMNode(this) as HTMLElement;
if (!e || e.target !== node) {
return;
}
if (!this.animationStart) {
this.resetEffect(node);
}
};
onTransitionEnd = (e: AnimationEvent) => {
if (!e || e.animationName !== 'fadeEffect') {
return;
}
this.resetEffect(e.target as HTMLElement);
};
getAttributeName() {
const { insertExtraNode } = this.props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
}
bindAnimationEvent = (node: HTMLElement) => {
if (
!node ||
@ -113,11 +163,6 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
};
};
getAttributeName() {
const { insertExtraNode } = this.props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
}
resetEffect(node: HTMLElement) {
if (!node || node === this.extraNode || !(node instanceof Element)) {
return;
@ -125,7 +170,11 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
const { insertExtraNode } = this.props;
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
this.removeExtraStyleNode();
if (styleForPesudo) {
styleForPesudo.innerHTML = '';
}
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
node.removeChild(this.extraNode);
}
@ -133,51 +182,6 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
}
onTransitionStart = (e: AnimationEvent) => {
if (this.destroy) return;
const node = findDOMNode(this) as HTMLElement;
if (!e || e.target !== node) {
return;
}
if (!this.animationStart) {
this.resetEffect(node);
}
};
onTransitionEnd = (e: AnimationEvent) => {
if (!e || e.animationName !== 'fadeEffect') {
return;
}
this.resetEffect(e.target as HTMLElement);
};
removeExtraStyleNode() {
if (styleForPesudo) {
styleForPesudo.innerHTML = '';
}
}
componentDidMount() {
const node = findDOMNode(this) as HTMLElement;
if (!node || node.nodeType !== 1) {
return;
}
this.instance = this.bindAnimationEvent(node);
}
componentWillUnmount() {
if (this.instance) {
this.instance.cancel();
}
if (this.clickWaveTimeoutId) {
clearTimeout(this.clickWaveTimeoutId);
}
this.destroy = true;
}
renderWave = ({ csp }: ConfigConsumerProps) => {
const { children } = this.props;
this.csp = csp;

View File

@ -63,7 +63,9 @@ class Affix extends React.Component<AffixProps, AffixState> {
};
placeholderNode: HTMLDivElement;
fixedNode: HTMLDivElement;
private timeout: number;
// Event handler
@ -75,7 +77,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
this.timeout = setTimeout(() => {
addObserveTarget(target(), this);
// Mock Event object.
this.updatePosition({} as Event);
this.updatePosition();
});
}
}
@ -93,7 +95,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
if (newTarget) {
addObserveTarget(newTarget, this);
// Mock Event object.
this.updatePosition({} as Event);
this.updatePosition();
}
this.setState({ prevTarget: newTarget });
@ -103,7 +105,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
prevProps.offsetTop !== this.props.offsetTop ||
prevProps.offsetBottom !== this.props.offsetBottom
) {
this.updatePosition({} as Event);
this.updatePosition();
}
this.measure();
@ -132,6 +134,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
}
return offsetTop;
};
getOffsetBottom = () => {
return this.props.offsetBottom;
};
@ -145,60 +148,6 @@ class Affix extends React.Component<AffixProps, AffixState> {
};
// =================== Measure ===================
// Handle realign logic
@throttleByAnimationFrameDecorator()
updatePosition(event?: Event | null) {
this.prepareMeasure(event);
}
@throttleByAnimationFrameDecorator()
lazyUpdatePosition(event: Event) {
const { target } = this.props;
const { affixStyle } = this.state;
// Check position change before measure to make Safari smooth
if (target && affixStyle) {
const offsetTop = this.getOffsetTop();
const offsetBottom = this.getOffsetBottom();
const targetNode = target();
if (targetNode) {
const targetRect = getTargetRect(targetNode);
const placeholderReact = getTargetRect(this.placeholderNode);
const fixedTop = getFixedTop(placeholderReact, targetRect, offsetTop);
const fixedBottom = getFixedBottom(placeholderReact, targetRect, offsetBottom);
if (
(fixedTop !== undefined && affixStyle.top === fixedTop) ||
(fixedBottom !== undefined && affixStyle.bottom === fixedBottom)
) {
return;
}
}
}
// Directly call prepare measure since it's already throttled.
this.prepareMeasure(event);
}
// @ts-ignore TS6133
prepareMeasure = (event?: Event | null) => {
// event param is used before. Keep compatible ts define here.
this.setState({
status: AffixStatus.Prepare,
affixStyle: undefined,
placeholderStyle: undefined,
});
// Test if `updatePosition` called
if (process.env.NODE_ENV === 'test') {
const { onTestUpdatePosition } = this.props as any;
if (onTestUpdatePosition) {
onTestUpdatePosition();
}
}
};
measure = () => {
const { status, lastAffix } = this.state;
const { target, onChange } = this.props;
@ -254,6 +203,60 @@ class Affix extends React.Component<AffixProps, AffixState> {
this.setState(newState as AffixState);
};
// @ts-ignore TS6133
prepareMeasure = () => {
// event param is used before. Keep compatible ts define here.
this.setState({
status: AffixStatus.Prepare,
affixStyle: undefined,
placeholderStyle: undefined,
});
// Test if `updatePosition` called
if (process.env.NODE_ENV === 'test') {
const { onTestUpdatePosition } = this.props as any;
if (onTestUpdatePosition) {
onTestUpdatePosition();
}
}
};
// Handle realign logic
@throttleByAnimationFrameDecorator()
updatePosition() {
this.prepareMeasure();
}
@throttleByAnimationFrameDecorator()
lazyUpdatePosition() {
const { target } = this.props;
const { affixStyle } = this.state;
// Check position change before measure to make Safari smooth
if (target && affixStyle) {
const offsetTop = this.getOffsetTop();
const offsetBottom = this.getOffsetBottom();
const targetNode = target();
if (targetNode) {
const targetRect = getTargetRect(targetNode);
const placeholderReact = getTargetRect(this.placeholderNode);
const fixedTop = getFixedTop(placeholderReact, targetRect, offsetTop);
const fixedBottom = getFixedBottom(placeholderReact, targetRect, offsetBottom);
if (
(fixedTop !== undefined && affixStyle.top === fixedTop) ||
(fixedBottom !== undefined && affixStyle.bottom === fixedBottom)
) {
return;
}
}
}
// Directly call prepare measure since it's already throttled.
this.prepareMeasure();
}
// =================== Render ===================
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
const { affixStyle, placeholderStyle } = this.state;

View File

@ -74,9 +74,9 @@ export function addObserveTarget(target: HTMLElement | Window | null, affix: Aff
// Add listener
TRIGGER_EVENTS.forEach(eventName => {
entity!.eventHandlers[eventName] = addEventListener(target, eventName, (event: Event) => {
entity!.affixList.forEach(affix => {
affix.lazyUpdatePosition(event);
entity!.eventHandlers[eventName] = addEventListener(target, eventName, () => {
entity!.affixList.forEach(targetAffix => {
targetAffix.lazyUpdatePosition();
});
});
});

View File

@ -69,6 +69,7 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
<span
class="ant-alert-close-icon"
role="button"
tabindex="0"
>
<i
aria-label="icon: close"
@ -174,6 +175,7 @@ exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
<span
class="ant-alert-close-icon"
role="button"
tabindex="0"
>
<i
aria-label="icon: close"
@ -213,6 +215,7 @@ exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
<span
class="ant-alert-close-icon"
role="button"
tabindex="0"
>
<i
aria-label="icon: close"
@ -254,6 +257,7 @@ exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `
<span
class="ant-alert-close-icon"
role="button"
tabindex="0"
>
<span
class="ant-alert-close-text"
@ -909,6 +913,7 @@ exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `
<span
class="ant-alert-close-icon"
role="button"
tabindex="0"
>
<i
aria-label="icon: close"

View File

@ -1,8 +1,8 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import Animate from 'rc-animate';
import Icon, { ThemeType } from '../icon';
import classNames from 'classnames';
import Icon, { ThemeType } from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
import warning from '../_util/warning';
@ -119,7 +119,7 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
}
// use outline icon in alert with description
if (!!description) {
if (description) {
iconTheme = 'outlined';
}
}
@ -143,7 +143,12 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
);
const closeIcon = closable ? (
<span role="button" onClick={this.handleClose} className={`${prefixCls}-close-icon`}>
<span
role="button"
onClick={this.handleClose}
className={`${prefixCls}-close-icon`}
tabIndex={0}
>
{closeText ? (
<span className={`${prefixCls}-close-text`}>{closeText}</span>
) : (

View File

@ -3,11 +3,11 @@ import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import raf from 'raf';
import Affix from '../affix';
import AnchorLink from './AnchorLink';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getScroll from '../_util/getScroll';
import raf from 'raf';
function getDefaultContainer() {
return window;
@ -145,11 +145,14 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
};
private inkNode: HTMLSpanElement;
// scroll scope's container
private scrollContainer: HTMLElement | Window;
private links: string[] = [];
private scrollEvent: any;
private animating: boolean;
private prefixCls?: string;
@ -181,12 +184,6 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
this.handleScroll();
}
componentWillUnmount() {
if (this.scrollEvent) {
this.scrollEvent.remove();
}
}
componentDidUpdate() {
if (this.scrollEvent) {
const { getContainer } = this.props as AnchorDefaultProps;
@ -201,28 +198,11 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
this.updateInk();
}
handleScroll = () => {
if (this.animating) {
return;
componentWillUnmount() {
if (this.scrollEvent) {
this.scrollEvent.remove();
}
const { activeLink } = this.state;
const { offsetTop, bounds } = this.props;
const currentActiveLink = this.getCurrentAnchor(offsetTop, bounds);
if (activeLink !== currentActiveLink) {
this.setState({
activeLink: currentActiveLink,
});
}
};
handleScrollTo = (link: string) => {
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false;
});
};
}
getCurrentAnchor(offsetTop = 0, bounds = 5): string {
const activeLink = '';
@ -257,11 +237,38 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
return '';
}
saveInkNode = (node: HTMLSpanElement) => {
this.inkNode = node;
};
handleScroll = () => {
if (this.animating) {
return;
}
const { activeLink } = this.state;
const { offsetTop, bounds } = this.props;
const currentActiveLink = this.getCurrentAnchor(offsetTop, bounds);
if (activeLink !== currentActiveLink) {
this.setState({
activeLink: currentActiveLink,
});
}
};
handleScrollTo = (link: string) => {
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false;
});
};
updateInk = () => {
if (typeof document === 'undefined') {
return;
}
const prefixCls = this.prefixCls;
const { prefixCls } = this;
const anchorNode = ReactDOM.findDOMNode(this) as Element;
const linkNode = anchorNode.getElementsByClassName(`${prefixCls}-link-title-active`)[0];
if (linkNode) {
@ -269,10 +276,6 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}
};
saveInkNode = (node: HTMLSpanElement) => {
this.inkNode = node;
};
renderAnchor = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,

View File

@ -9,13 +9,19 @@ export default class InputElement extends React.Component<InputElementProps, any
private ele: HTMLInputElement;
focus = () => {
this.ele.focus
? this.ele.focus()
: (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
if (this.ele.focus) {
this.ele.focus();
} else {
(ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
}
};
blur = () => {
this.ele.blur ? this.ele.blur() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur();
if (this.ele.blur) {
this.ele.blur();
} else {
(ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur();
}
};
saveRef = (ele: HTMLInputElement) => {

View File

@ -51,6 +51,7 @@ function isSelectOptionOrSelectOptGroup(child: any): Boolean {
export default class AutoComplete extends React.Component<AutoCompleteProps, {}> {
static Option = Option as React.ClassicComponentClass<OptionProps>;
static OptGroup = OptGroup as React.ClassicComponentClass<OptGroupProps>;
static defaultProps = {
@ -63,6 +64,10 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
private select: any;
saveSelect = (node: any) => {
this.select = node;
};
getInputElement = () => {
const { children } = this.props;
const element =
@ -85,10 +90,6 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
this.select.blur();
}
saveSelect = (node: any) => {
this.select = node;
};
renderAutoComplete = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import Icon from '../icon';
import classNames from 'classnames';
import Icon from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export interface AvatarProps {
@ -23,7 +23,7 @@ export interface AvatarProps {
children?: React.ReactNode;
alt?: string;
/* callback when img load error */
/* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self*/
/* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self */
onError?: () => boolean;
}
@ -44,8 +44,11 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
};
private avatarNode: HTMLElement;
private avatarChildren: HTMLElement;
private lastChildrenWidth: number;
private lastNodeWidth: number;
componentDidMount() {
@ -127,7 +130,7 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
}
: {};
let children = this.props.children;
let { children } = this.props;
if (src && isImgExist) {
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
} else if (icon) {

View File

@ -12,9 +12,8 @@ const easeInOutCubic = (t: number, b: number, c: number, d: number) => {
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
} else {
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
};
function noop() {}
@ -47,6 +46,29 @@ export default class BackTop extends React.Component<BackTopProps, any> {
};
}
componentDidMount() {
const getTarget = this.props.target || getDefaultTarget;
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll);
this.handleScroll();
}
componentWillUnmount() {
if (this.scrollEvent) {
this.scrollEvent.remove();
}
}
setScrollTop(value: number) {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
document.body.scrollTop = value;
document.documentElement!.scrollTop = value;
} else {
(targetNode as HTMLElement).scrollTop = value;
}
}
getCurrentScrollTop = () => {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
@ -73,17 +95,6 @@ export default class BackTop extends React.Component<BackTopProps, any> {
(this.props.onClick || noop)(e);
};
setScrollTop(value: number) {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
document.body.scrollTop = value;
document.documentElement!.scrollTop = value;
} else {
(targetNode as HTMLElement).scrollTop = value;
}
}
handleScroll = () => {
const { visibilityHeight, target = getDefaultTarget } = this.props;
const scrollTop = getScroll(target(), true);
@ -92,18 +103,6 @@ export default class BackTop extends React.Component<BackTopProps, any> {
});
};
componentDidMount() {
const getTarget = this.props.target || getDefaultTarget;
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll);
this.handleScroll();
}
componentWillUnmount() {
if (this.scrollEvent) {
this.scrollEvent.remove();
}
}
renderBackTop = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className = '', children } = this.props;
const prefixCls = getPrefixCls('back-top', customizePrefixCls);

View File

@ -1,9 +1,8 @@
import * as React from 'react';
import { createElement, Component } from 'react';
import React, { createElement, Component } from 'react';
import omit from 'omit.js';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { polyfill } from 'react-lifecycles-compat';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
function getNumberArray(num: string | number | undefined | null) {
return num
@ -18,6 +17,20 @@ function getNumberArray(num: string | number | undefined | null) {
: [];
}
function renderNumberList(position: number) {
const childrenToReturn: React.ReactElement<any>[] = [];
for (let i = 0; i < 30; i++) {
const currentClassName = position === i ? 'current' : '';
childrenToReturn.push(
<p key={i.toString()} className={currentClassName}>
{i % 10}
</p>,
);
}
return childrenToReturn;
}
export interface ScrollNumberProps {
prefixCls?: string;
className?: string;
@ -62,6 +75,21 @@ class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
};
}
componentDidUpdate(_: any, prevState: ScrollNumberState) {
this.lastCount = prevState.count;
const { animateStarted } = this.state;
if (animateStarted) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState(
(__, props) => ({
animateStarted: false,
count: props.count,
}),
this.onAnimated,
);
}
}
getPositionByNum(num: number, i: number) {
const { count } = this.state;
const currentCount = Math.abs(Number(count));
@ -86,20 +114,6 @@ class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
return num;
}
componentDidUpdate(_: any, prevState: ScrollNumberState) {
this.lastCount = prevState.count;
const { animateStarted } = this.state;
if (animateStarted) {
this.setState(
(__, props) => ({
animateStarted: false,
count: props.count,
}),
this.onAnimated,
);
}
}
onAnimated = () => {
const { onAnimated } = this.props;
if (onAnimated) {
@ -107,20 +121,6 @@ class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
}
};
renderNumberList(position: number) {
const childrenToReturn: React.ReactElement<any>[] = [];
for (let i = 0; i < 30; i++) {
const currentClassName = position === i ? 'current' : '';
childrenToReturn.push(
<p key={i.toString()} className={currentClassName}>
{i % 10}
</p>,
);
}
return childrenToReturn;
}
renderCurrentNumber(prefixCls: string, num: number | string, i: number) {
if (typeof num === 'number') {
const position = this.getPositionByNum(num, i);
@ -138,7 +138,7 @@ class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
},
key: i,
},
this.renderNumberList(position),
renderNumberList(position),
);
}

View File

@ -1,6 +1,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import Animate from 'rc-animate';
import omit from 'omit.js';
import classNames from 'classnames';
import ScrollNumber from './ScrollNumber';
import { PresetColorTypes } from '../_util/colors';
@ -46,39 +47,6 @@ export default class Badge extends React.Component<BadgeProps, any> {
overflowCount: PropTypes.number,
};
getBadgeClassName(prefixCls: string) {
const { className, children } = this.props;
return classNames(className, prefixCls, {
[`${prefixCls}-status`]: this.hasStatus(),
[`${prefixCls}-not-a-wrapper`]: !children,
}) as string;
}
hasStatus(): boolean {
const { status, color } = this.props;
return !!status || !!color;
}
isZero() {
const numberedDispayCount = this.getNumberedDispayCount();
return numberedDispayCount === '0' || numberedDispayCount === 0;
}
isDot() {
const { dot } = this.props;
const isZero = this.isZero();
return (dot && !isZero) || this.hasStatus();
}
isHidden() {
const { showZero } = this.props;
const displayCount = this.getDispayCount();
const isZero = this.isZero();
const isDot = this.isDot();
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
return (isEmpty || (isZero && !showZero)) && !isDot;
}
getNumberedDispayCount() {
const { count, overflowCount } = this.props;
const displayCount =
@ -114,6 +82,39 @@ export default class Badge extends React.Component<BadgeProps, any> {
: style;
}
getBadgeClassName(prefixCls: string) {
const { className, children } = this.props;
return classNames(className, prefixCls, {
[`${prefixCls}-status`]: this.hasStatus(),
[`${prefixCls}-not-a-wrapper`]: !children,
}) as string;
}
hasStatus(): boolean {
const { status, color } = this.props;
return !!status || !!color;
}
isZero() {
const numberedDispayCount = this.getNumberedDispayCount();
return numberedDispayCount === '0' || numberedDispayCount === 0;
}
isDot() {
const { dot } = this.props;
const isZero = this.isZero();
return (dot && !isZero) || this.hasStatus();
}
isHidden() {
const { showZero } = this.props;
const displayCount = this.getDispayCount();
const isZero = this.isZero();
const isDot = this.isDot();
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
return (isEmpty || (isZero && !showZero)) && !isDot;
}
renderStatusText(prefixCls: string) {
const { text } = this.props;
const hidden = this.isHidden();
@ -165,22 +166,24 @@ export default class Badge extends React.Component<BadgeProps, any> {
renderBadge = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
count,
showZero,
prefixCls: customizePrefixCls,
scrollNumberPrefixCls: customizeScrollNumberPrefixCls,
overflowCount,
className,
style,
children,
dot,
status,
text,
offset,
title,
color,
...restProps
} = this.props;
const omitArr = [
'count',
'showZero',
'overflowCount',
'className',
'style',
'dot',
'offset',
'title',
];
const prefixCls = getPrefixCls('badge', customizePrefixCls);
const scrollNumberPrefixCls = getPrefixCls('scroll-number', customizeScrollNumberPrefixCls);
@ -203,7 +206,11 @@ export default class Badge extends React.Component<BadgeProps, any> {
const styleWithOffset = this.getStyleWithOffset();
const statusTextColor = styleWithOffset && styleWithOffset.color;
return (
<span {...restProps} className={this.getBadgeClassName(prefixCls)} style={styleWithOffset}>
<span
{...omit(restProps, omitArr)}
className={this.getBadgeClassName(prefixCls)}
style={styleWithOffset}
>
<span className={statusCls} style={statusStyle} />
<span style={{ color: statusTextColor }} className={`${prefixCls}-status-text`}>
{text}
@ -213,7 +220,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
}
return (
<span {...restProps} className={this.getBadgeClassName(prefixCls)}>
<span {...omit(restProps, omitArr)} className={this.getBadgeClassName(prefixCls)}>
{children}
<Animate
component=""

View File

@ -1,6 +1,5 @@
import * as React from 'react';
import React, { cloneElement } from 'react';
import * as PropTypes from 'prop-types';
import { cloneElement } from 'react';
import classNames from 'classnames';
import BreadcrumbItem from './BreadcrumbItem';
import BreadcrumbSeparator from './BreadcrumbSeparator';
@ -50,6 +49,7 @@ function defaultItemRender(route: Route, params: any, routes: Route[], paths: st
export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
static Item: typeof BreadcrumbItem;
static Separator: typeof BreadcrumbSeparator;
static defaultProps = {
@ -60,11 +60,10 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
prefixCls: PropTypes.string,
separator: PropTypes.node,
routes: PropTypes.array,
params: PropTypes.object,
};
componentDidMount() {
const props = this.props;
const { props } = this;
warning(
!('linkRender' in props || 'nameRender' in props),
'Breadcrumb',
@ -124,6 +123,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
);
});
};
renderBreadcrumb = ({ getPrefixCls }: ConfigConsumerProps) => {
let crumbs;
const {

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import omit from 'omit.js';
import DropDown, { DropDownProps } from '../dropdown/dropdown';
import Icon from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
@ -26,24 +27,18 @@ export default class BreadcrumbItem extends React.Component<BreadcrumbItemProps,
};
renderBreadcrumbItem = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
separator,
children,
overlay,
...restProps
} = this.props;
const { prefixCls: customizePrefixCls, separator, children, ...restProps } = this.props;
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
let link;
if ('href' in this.props) {
link = (
<a className={`${prefixCls}-link`} {...restProps}>
<a className={`${prefixCls}-link`} {...omit(restProps, ['overlay'])}>
{children}
</a>
);
} else {
link = (
<span className={`${prefixCls}-link`} {...restProps}>
<span className={`${prefixCls}-link`} {...omit(restProps, ['overlay'])}>
{children}
</span>
);
@ -82,6 +77,7 @@ export default class BreadcrumbItem extends React.Component<BreadcrumbItemProps,
}
return breadcrumbItem;
};
render() {
return <ConfigConsumer>{this.renderBreadcrumbItem}</ConfigConsumer>;
}

View File

@ -25,6 +25,7 @@ const ButtonGroup: React.SFC<ButtonGroupProps> = props => (
break;
case 'small':
sizeCls = 'sm';
break;
default:
break;
}

View File

@ -1,9 +1,10 @@
/* eslint-disable react/button-has-type */
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import { polyfill } from 'react-lifecycles-compat';
import Group from './button-group';
import omit from 'omit.js';
import Group from './button-group';
import Icon from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import Wave from '../_util/wave';
@ -15,29 +16,6 @@ function isString(str: any) {
return typeof str === 'string';
}
function spaceChildren(children: React.ReactNode, needInserted: boolean) {
let isPrevChildPure: boolean = false;
const childList: React.ReactNode[] = [];
React.Children.forEach(children, child => {
const type = typeof child;
const isCurrentChildPure = type === 'string' || type === 'number';
if (isPrevChildPure && isCurrentChildPure) {
const lastIndex = childList.length - 1;
const lastChild = childList[lastIndex];
childList[lastIndex] = `${lastChild}${child}`;
} else {
childList.push(child);
}
isPrevChildPure = isCurrentChildPure;
});
// Pass to React.Children.map to auto fill key
return React.Children.map(childList, child =>
insertSpace(child as React.ReactChild, needInserted),
);
}
// Insert one space between two chinese characters automatically.
function insertSpace(child: React.ReactChild, needInserted: boolean) {
// Check the child if is undefined or null.
@ -63,6 +41,29 @@ function insertSpace(child: React.ReactChild, needInserted: boolean) {
return child;
}
function spaceChildren(children: React.ReactNode, needInserted: boolean) {
let isPrevChildPure: boolean = false;
const childList: React.ReactNode[] = [];
React.Children.forEach(children, child => {
const type = typeof child;
const isCurrentChildPure = type === 'string' || type === 'number';
if (isPrevChildPure && isCurrentChildPure) {
const lastIndex = childList.length - 1;
const lastChild = childList[lastIndex];
childList[lastIndex] = `${lastChild}${child}`;
} else {
childList.push(child);
}
isPrevChildPure = isCurrentChildPure;
});
// Pass to React.Children.map to auto fill key
return React.Children.map(childList, child =>
insertSpace(child as React.ReactChild, needInserted),
);
}
const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'danger', 'link');
export type ButtonType = (typeof ButtonTypes)[number];
const ButtonShapes = tuple('circle', 'circle-outline', 'round');
@ -110,6 +111,7 @@ interface ButtonState {
class Button extends React.Component<ButtonProps, ButtonState> {
static Group: typeof Group;
static __ANT_BUTTON = true;
static defaultProps = {
@ -142,6 +144,7 @@ class Button extends React.Component<ButtonProps, ButtonState> {
}
private delayTimeout: number;
private buttonNode: HTMLElement | null;
constructor(props: ButtonProps) {
@ -166,9 +169,8 @@ class Button extends React.Component<ButtonProps, ButtonState> {
const { loading } = this.props;
if (loading && typeof loading !== 'boolean' && loading.delay) {
this.delayTimeout = window.setTimeout(() => this.setState({ loading }), loading.delay);
} else if (prevProps.loading === this.props.loading) {
return;
} else {
} else if (prevProps.loading !== this.props.loading) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ loading });
}
}
@ -183,6 +185,17 @@ class Button extends React.Component<ButtonProps, ButtonState> {
this.buttonNode = node;
};
handleClick: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> = e => {
const { loading } = this.state;
const { onClick } = this.props;
if (loading) {
return;
}
if (onClick) {
(onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)(e);
}
};
fixTwoCNChar() {
// Fix for HOC usage like <FormatMessage />
if (!this.buttonNode) {
@ -202,17 +215,6 @@ class Button extends React.Component<ButtonProps, ButtonState> {
}
}
handleClick: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> = e => {
const { loading } = this.state;
const { onClick } = this.props;
if (!!loading) {
return;
}
if (onClick) {
(onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)(e);
}
};
isNeedInserted() {
const { icon, children } = this.props;
return React.Children.count(children) === 1 && !icon;
@ -228,7 +230,6 @@ class Button extends React.Component<ButtonProps, ButtonState> {
children,
icon,
ghost,
loading: _loadingProp,
block,
...rest
} = this.props;
@ -289,7 +290,7 @@ class Button extends React.Component<ButtonProps, ButtonState> {
const buttonNode = (
<button
{...(otherProps as NativeButtonProps)}
{...(omit(otherProps, ['loading']) as NativeButtonProps)}
type={htmlType}
className={classes}
onClick={this.handleClick}

View File

@ -3,8 +3,20 @@ import * as moment from 'moment';
import Select from '../select';
import { Group, Button, RadioChangeEvent } from '../radio';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
const { Option } = Select;
function getMonthsLocale(value: moment.Moment) {
const current = value.clone();
const localeData = value.localeData();
const months: any[] = [];
for (let i = 0; i < 12; i++) {
current.month(i);
months.push(localeData.monthsShort(current));
}
return months;
}
export interface RenderHeader {
value: moment.Moment;
onChange?: (value: moment.Moment) => void;
@ -63,17 +75,6 @@ export default class Header extends React.Component<HeaderProps, any> {
);
}
getMonthsLocale(value: moment.Moment) {
const current = value.clone();
const localeData = value.localeData();
const months: any[] = [];
for (let i = 0; i < 12; i++) {
current.month(i);
months.push(localeData.monthsShort(current));
}
return months;
}
getMonthSelectElement(prefixCls: string, month: number, months: number[]) {
const { fullscreen, validRange, value } = this.props;
const options: React.ReactElement<any>[] = [];
@ -124,7 +125,7 @@ export default class Header extends React.Component<HeaderProps, any> {
}
}
const onValueChange = this.props.onValueChange;
const { onValueChange } = this.props;
if (onValueChange) {
onValueChange(newValue);
}
@ -133,7 +134,7 @@ export default class Header extends React.Component<HeaderProps, any> {
onMonthChange = (month: string) => {
const newValue = this.props.value.clone();
newValue.month(parseInt(month, 10));
const onValueChange = this.props.onValueChange;
const { onValueChange } = this.props;
if (onValueChange) {
onValueChange(newValue);
}
@ -144,7 +145,7 @@ export default class Header extends React.Component<HeaderProps, any> {
};
onTypeChange = (type: string) => {
const onTypeChange = this.props.onTypeChange;
const { onTypeChange } = this.props;
if (onTypeChange) {
onTypeChange(type);
}
@ -161,7 +162,7 @@ export default class Header extends React.Component<HeaderProps, any> {
const yearReactNode = this.getYearSelectElement(prefixCls, value.year());
const monthReactNode =
type === 'month'
? this.getMonthSelectElement(prefixCls, value.month(), this.getMonthsLocale(value))
? this.getMonthSelectElement(prefixCls, value.month(), getMonthsLocale(value))
: null;
return {
yearReactNode,

View File

@ -2,12 +2,12 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import * as moment from 'moment';
import FullCalendar from 'rc-calendar/lib/FullCalendar';
import { polyfill } from 'react-lifecycles-compat';
import Header, { HeaderRender } from './Header';
import enUS from './locale/en_US';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import interopDefault from '../_util/interopDefault';
import { polyfill } from 'react-lifecycles-compat';
export { HeaderProps } from './Header';
@ -104,47 +104,6 @@ class Calendar extends React.Component<CalendarProps, CalendarState> {
};
}
monthCellRender = (value: moment.Moment) => {
const { monthCellRender = noop as Function } = this.props;
const { prefixCls } = this;
return (
<div className={`${prefixCls}-month`}>
<div className={`${prefixCls}-value`}>{value.localeData().monthsShort(value)}</div>
<div className={`${prefixCls}-content`}>{monthCellRender(value)}</div>
</div>
);
};
dateCellRender = (value: moment.Moment) => {
const { dateCellRender = noop as Function } = this.props;
const { prefixCls } = this;
return (
<div className={`${prefixCls}-date`}>
<div className={`${prefixCls}-value`}>{zerofixed(value.date())}</div>
<div className={`${prefixCls}-content`}>{dateCellRender(value)}</div>
</div>
);
};
setValue = (value: moment.Moment, way: 'select' | 'changePanel') => {
const prevValue = this.props.value || this.state.value;
const { mode } = this.state;
if (!('value' in this.props)) {
this.setState({ value });
}
if (way === 'select') {
if (prevValue && prevValue.month() !== value.month()) {
this.onPanelChange(value, mode);
}
if (this.props.onSelect) {
this.props.onSelect(value);
}
} else if (way === 'changePanel') {
this.onPanelChange(value, mode);
}
};
onHeaderValueChange = (value: moment.Moment) => {
this.setValue(value, 'changePanel');
};
@ -168,6 +127,25 @@ class Calendar extends React.Component<CalendarProps, CalendarState> {
this.setValue(value, 'select');
};
setValue = (value: moment.Moment, way: 'select' | 'changePanel') => {
const prevValue = this.props.value || this.state.value;
const { mode } = this.state;
if (!('value' in this.props)) {
this.setState({ value });
}
if (way === 'select') {
if (prevValue && prevValue.month() !== value.month()) {
this.onPanelChange(value, mode);
}
if (this.props.onSelect) {
this.props.onSelect(value);
}
} else if (way === 'changePanel') {
this.onPanelChange(value, mode);
}
};
getDateRange = (
validRange: [moment.Moment, moment.Moment],
disabledDate?: (current: moment.Moment) => boolean,
@ -195,6 +173,28 @@ class Calendar extends React.Component<CalendarProps, CalendarState> {
return result;
};
monthCellRender = (value: moment.Moment) => {
const { monthCellRender = noop as Function } = this.props;
const { prefixCls } = this;
return (
<div className={`${prefixCls}-month`}>
<div className={`${prefixCls}-value`}>{value.localeData().monthsShort(value)}</div>
<div className={`${prefixCls}-content`}>{monthCellRender(value)}</div>
</div>
);
};
dateCellRender = (value: moment.Moment) => {
const { dateCellRender = noop as Function } = this.props;
const { prefixCls } = this;
return (
<div className={`${prefixCls}-date`}>
<div className={`${prefixCls}-value`}>{zerofixed(value.date())}</div>
<div className={`${prefixCls}-content`}>{dateCellRender(value)}</div>
</div>
);
};
renderCalendar = (locale: any, localeCode: string) => {
const { state, props } = this;
const { value, mode } = state;
@ -213,7 +213,7 @@ class Calendar extends React.Component<CalendarProps, CalendarState> {
const monthCellRender = monthFullCellRender || this.monthCellRender;
const dateCellRender = dateFullCellRender || this.dateCellRender;
let disabledDate = props.disabledDate;
let { disabledDate } = props;
if (props.validRange) {
disabledDate = this.getDateRange(props.validRange, disabledDate);

View File

@ -46,7 +46,11 @@ class App extends React.Component {
<Card
style={{ width: 300, marginTop: 16 }}
actions={[<Icon type="setting" />, <Icon type="edit" />, <Icon type="ellipsis" />]}
actions={[
<Icon type="setting" key="setting" />,
<Icon type="edit" key="edit" />,
<Icon type="ellipsis" key="ellipsis" />,
]}
>
<Skeleton loading={loading} avatar active>
<Meta

View File

@ -27,7 +27,11 @@ ReactDOM.render(
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[<Icon type="setting" />, <Icon type="edit" />, <Icon type="ellipsis" />]}
actions={[
<Icon type="setting" key="setting" />,
<Icon type="edit" key="edit" />,
<Icon type="ellipsis" key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}

View File

@ -10,6 +10,16 @@ import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import warning from '../_util/warning';
import { Omit } from '../_util/type';
function getAction(actions: React.ReactNode[]) {
const actionList = actions.map((action, index) => (
// eslint-disable-next-line react/no-array-index-key
<li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}>
<span>{action}</span>
</li>
));
return actionList;
}
export { CardGridProps } from './Grid';
export { CardMetaProps } from './Meta';
@ -48,6 +58,7 @@ export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 't
export default class Card extends React.Component<CardProps, {}> {
static Grid: typeof Grid = Grid;
static Meta: typeof Meta = Meta;
componentDidMount() {
@ -65,6 +76,15 @@ export default class Card extends React.Component<CardProps, {}> {
}
}
// For 2.x compatible
getCompatibleHoverable() {
const { noHovering, hoverable } = this.props;
if ('noHovering' in this.props) {
return !noHovering || hoverable;
}
return !!hoverable;
}
onTabChange = (key: string) => {
if (this.props.onTabChange) {
this.props.onTabChange(key);
@ -81,24 +101,6 @@ export default class Card extends React.Component<CardProps, {}> {
return containGrid;
}
getAction(actions: React.ReactNode[]) {
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;
}
renderCard = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
@ -106,8 +108,6 @@ export default class Card extends React.Component<CardProps, {}> {
extra,
headStyle = {},
bodyStyle = {},
noHovering,
hoverable,
title,
loading,
bordered = true,
@ -221,9 +221,9 @@ export default class Card extends React.Component<CardProps, {}> {
);
const actionDom =
actions && actions.length ? (
<ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
<ul className={`${prefixCls}-actions`}>{getAction(actions)}</ul>
) : null;
const divProps = omit(others, ['onTabChange']);
const divProps = omit(others, ['onTabChange', 'noHovering', 'hoverable']);
return (
<div {...divProps} className={classString}>
{head}

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import debounce from 'lodash/debounce';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { Settings } from 'react-slick';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import warning from '../_util/warning';
// matchMedia polyfill for
@ -85,6 +85,20 @@ export default class Carousel extends React.Component<CarouselProps, {}> {
}
}
getDotPosition(): DotPosition {
if (this.props.dotPosition) {
return this.props.dotPosition;
}
if ('vertical' in this.props) {
return this.props.vertical ? 'right' : 'bottom';
}
return 'bottom';
}
saveSlick = (node: any) => {
this.slick = node;
};
onWindowResized = () => {
// Fix https://github.com/ant-design/ant-design/issues/2550
const { autoplay } = this.props;
@ -93,10 +107,6 @@ export default class Carousel extends React.Component<CarouselProps, {}> {
}
};
saveSlick = (node: any) => {
this.slick = node;
};
next() {
this.slick.slickNext();
}
@ -109,15 +119,6 @@ export default class Carousel extends React.Component<CarouselProps, {}> {
this.slick.slickGoTo(slide, dontAnimate);
}
getDotPosition(): DotPosition {
if (this.props.dotPosition) {
return this.props.dotPosition;
} else if ('vertical' in this.props) {
return this.props.vertical ? 'right' : 'bottom';
}
return 'bottom';
}
renderCarousel = ({ getPrefixCls }: ConfigConsumerProps) => {
const props = {
...this.props,

View File

@ -1218,7 +1218,7 @@ exports[`Cascader should render not found content 1`] = `
>
<Empty
className="ant-empty-small"
image={<_default />}
image={<Simple />}
>
<LocaleReceiver
componentName="Empty"
@ -1229,7 +1229,7 @@ exports[`Cascader should render not found content 1`] = `
<div
className="ant-empty-image"
>
<_default>
<Simple>
<svg
height="41"
viewBox="0 0 64 41"
@ -1262,7 +1262,7 @@ exports[`Cascader should render not found content 1`] = `
</g>
</g>
</svg>
</_default>
</Simple>
</div>
<p
className="ant-empty-description"
@ -1541,7 +1541,7 @@ exports[`Cascader should show not found content when options.length is 0 1`] = `
>
<Empty
className="ant-empty-small"
image={<_default />}
image={<Simple />}
>
<LocaleReceiver
componentName="Empty"
@ -1552,7 +1552,7 @@ exports[`Cascader should show not found content when options.length is 0 1`] = `
<div
className="ant-empty-image"
>
<_default>
<Simple>
<svg
height="41"
viewBox="0 0 64 41"
@ -1585,7 +1585,7 @@ exports[`Cascader should show not found content when options.length is 0 1`] = `
</g>
</g>
</svg>
</_default>
</Simple>
</div>
<p
className="ant-empty-description"

View File

@ -72,13 +72,13 @@ export interface CascaderProps {
popupClassName?: string;
/** 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` */
popupPlacement?: string;
/** 输入框占位文本*/
/** 输入框占位文本 */
placeholder?: string;
/** 输入框大小,可选 `large` `default` `small` */
size?: string;
/** 禁用*/
/** 禁用 */
disabled?: boolean;
/** 是否支持清除*/
/** 是否支持清除 */
allowClear?: boolean;
showSearch?: boolean | ShowSearchType;
notFoundContent?: React.ReactNode;
@ -262,6 +262,34 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
};
}
setValue = (value: string[], selectedOptions: CascaderOptionType[] = []) => {
if (!('value' in this.props)) {
this.setState({ value });
}
const { onChange } = this.props;
if (onChange) {
onChange(value, selectedOptions);
}
};
getLabel() {
const { options, displayRender = defaultDisplayRender as Function } = this.props;
const names = getFilledFieldNames(this.props);
const { value } = this.state;
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
const selectedOptions: CascaderOptionType[] = arrayTreeFilter(
options,
(o: CascaderOptionType, level: number) => o[names.value] === unwrappedValue[level],
{ childrenKeyName: names.children },
);
const label = selectedOptions.map(o => o[names.label]);
return displayRender(label, selectedOptions);
}
saveInput = (node: Input) => {
this.input = node;
};
handleChange = (value: any, selectedOptions: CascaderOptionType[]) => {
this.setState({ inputValue: '' });
if (selectedOptions[0].__IS_FILTERED_OPTION) {
@ -282,7 +310,7 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
}));
}
const onPopupVisibleChange = this.props.onPopupVisibleChange;
const { onPopupVisibleChange } = this.props;
if (onPopupVisibleChange) {
onPopupVisibleChange(popupVisible);
}
@ -317,30 +345,6 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
this.setState({ inputValue });
};
setValue = (value: string[], selectedOptions: CascaderOptionType[] = []) => {
if (!('value' in this.props)) {
this.setState({ value });
}
const onChange = this.props.onChange;
if (onChange) {
onChange(value, selectedOptions);
}
};
getLabel() {
const { options, displayRender = defaultDisplayRender as Function } = this.props;
const names = getFilledFieldNames(this.props);
const value = this.state.value;
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
const selectedOptions: CascaderOptionType[] = arrayTreeFilter(
options,
(o: CascaderOptionType, level: number) => o[names.value] === unwrappedValue[level],
{ childrenKeyName: names.children },
);
const label = selectedOptions.map(o => o[names.label]);
return displayRender(label, selectedOptions);
}
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
@ -417,10 +421,6 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
this.input.blur();
}
saveInput = (node: Input) => {
this.input = node;
};
renderCascader = (
{ getPopupContainer: getContextPopupContainer, getPrefixCls, renderEmpty }: ConfigConsumerProps,
locale: CascaderLocale,
@ -495,7 +495,7 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
'filedNames', // For old compatibility
]);
let options = props.options;
let { options } = props;
if (options.length > 0) {
if (state.inputValue) {
options = this.generateFilteredOptions(prefixCls, renderEmpty);
@ -524,8 +524,7 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
dropdownMenuColumnStyle.height = 'auto'; // Height of one row.
}
// The default value of `matchInputWidth` is `true`
const resultListMatchInputWidth =
(showSearch as ShowSearchType).matchInputWidth === false ? false : true;
const resultListMatchInputWidth = (showSearch as ShowSearchType).matchInputWidth !== false;
if (resultListMatchInputWidth && (state.inputValue || isNotFound) && this.input) {
dropdownMenuColumnStyle.width = this.input.input.offsetWidth;
}

View File

@ -45,6 +45,7 @@ export interface CheckboxChangeEvent {
class Checkbox extends React.Component<CheckboxProps, {}> {
static Group: typeof CheckboxGroup;
static defaultProps = {
indeterminate: false,
};
@ -65,6 +66,18 @@ class Checkbox extends React.Component<CheckboxProps, {}> {
}
}
shouldComponentUpdate(
nextProps: CheckboxProps,
nextState: {},
nextContext: CheckboxGroupContext,
) {
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState) ||
!shallowEqual(this.context.checkboxGroup, nextContext.checkboxGroup)
);
}
componentDidUpdate({ value: prevValue }: CheckboxProps) {
const { value } = this.props;
const { checkboxGroup = {} } = this.context || {};
@ -82,17 +95,9 @@ class Checkbox extends React.Component<CheckboxProps, {}> {
}
}
shouldComponentUpdate(
nextProps: CheckboxProps,
nextState: {},
nextContext: CheckboxGroupContext,
) {
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState) ||
!shallowEqual(this.context.checkboxGroup, nextContext.checkboxGroup)
);
}
saveCheckbox = (node: any) => {
this.rcCheckbox = node;
};
focus() {
this.rcCheckbox.focus();
@ -102,10 +107,6 @@ class Checkbox extends React.Component<CheckboxProps, {}> {
this.rcCheckbox.blur();
}
saveCheckbox = (node: any) => {
this.rcCheckbox = node;
};
renderCheckbox = ({ getPrefixCls }: ConfigConsumerProps) => {
const { props, context } = this;
const {
@ -141,6 +142,7 @@ class Checkbox extends React.Component<CheckboxProps, {}> {
[`${prefixCls}-indeterminate`]: indeterminate,
});
return (
// eslint-disable-next-line jsx-a11y/label-has-associated-control
<label
className={classString}
style={style}

View File

@ -96,18 +96,6 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
}
registerValue = (value: string) => {
this.setState(({ registeredValues }) => ({
registeredValues: [...registeredValues, value],
}));
};
cancelValue = (value: string) => {
this.setState(({ registeredValues }) => ({
registeredValues: registeredValues.filter(val => val !== value),
}));
};
getOptions() {
const { options } = this.props;
// https://github.com/Microsoft/TypeScript/issues/7960
@ -122,6 +110,18 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
});
}
cancelValue = (value: string) => {
this.setState(({ registeredValues }) => ({
registeredValues: registeredValues.filter(val => val !== value),
}));
};
registerValue = (value: string) => {
this.setState(({ registeredValues }) => ({
registeredValues: [...registeredValues, value],
}));
};
toggleOption = (option: CheckboxOptionType) => {
const { registeredValues } = this.state;
const optionIndex = this.state.value.indexOf(option.value);
@ -134,7 +134,7 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
if (!('value' in this.props)) {
this.setState({ value });
}
const onChange = this.props.onChange;
const { onChange } = this.props;
if (onChange) {
const options = this.getOptions();
onChange(
@ -157,7 +157,7 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
const domProps = omit(restProps, ['children', 'defaultValue', 'value', 'onChange', 'disabled']);
let children = props.children;
let { children } = props;
if (options && options.length > 0) {
children = this.getOptions().map(option => (
<Checkbox

View File

@ -230,6 +230,7 @@ exports[`renders ./components/comment/demo/list.md correctly 1`] = `
class="ant-comment-avatar"
>
<img
alt="comment-avatar"
src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
</div>
@ -283,6 +284,7 @@ exports[`renders ./components/comment/demo/list.md correctly 1`] = `
class="ant-comment-avatar"
>
<img
alt="comment-avatar"
src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
</div>

View File

@ -44,7 +44,7 @@ class App extends React.Component {
const { likes, dislikes, action } = this.state;
const actions = [
<span>
<span key="comment-basic-like">
<Tooltip title="Like">
<Icon
type="like"
@ -54,7 +54,7 @@ class App extends React.Component {
</Tooltip>
<span style={{ paddingLeft: 8, cursor: 'auto' }}>{likes}</span>
</span>,
<span>
<span key=' key="comment-basic-dislike"'>
<Tooltip title="Dislike">
<Icon
type="dislike"
@ -64,7 +64,7 @@ class App extends React.Component {
</Tooltip>
<span style={{ paddingLeft: 8, cursor: 'auto' }}>{dislikes}</span>
</span>,
<span>Reply to</span>,
<span key="comment-basic-reply-to">Reply to</span>,
];
return (

View File

@ -19,7 +19,7 @@ import moment from 'moment';
const data = [
{
actions: [<span>Reply to</span>],
actions: [<span key="comment-list-reply-to-0">Reply to</span>],
author: 'Han Solo',
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: (
@ -44,7 +44,7 @@ const data = [
),
},
{
actions: [<span>Reply to</span>],
actions: [<span key="comment-list-reply-to-0">Reply to</span>],
author: 'Han Solo',
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
content: (

View File

@ -18,7 +18,7 @@ import { Comment, Avatar } from 'antd';
const ExampleComment = ({ children }) => (
<Comment
actions={[<span>Reply to</span>]}
actions={[<span key="comment-nested-reply-to">Reply to</span>]}
author={<a>Han Solo</a>}
avatar={
<Avatar

View File

@ -2,6 +2,15 @@ import * as React from 'react';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
function getAction(actions: React.ReactNode[]) {
if (!actions || !actions.length) {
return null;
}
// eslint-disable-next-line react/no-array-index-key
const actionList = actions.map((action, index) => <li key={`action-${index}`}>{action}</li>);
return actionList;
}
export interface CommentProps {
/** List of action items rendered below the comment content */
actions?: Array<React.ReactNode>;
@ -24,14 +33,6 @@ export interface CommentProps {
}
export default class Comment extends React.Component<CommentProps, {}> {
getAction(actions: React.ReactNode[]) {
if (!actions || !actions.length) {
return null;
}
const actionList = actions.map((action, index) => <li key={`action-${index}`}>{action}</li>);
return actionList;
}
renderNested = (prefixCls: string, children: any) => {
return <div className={classNames(`${prefixCls}-nested`)}>{children}</div>;
};
@ -54,13 +55,13 @@ export default class Comment extends React.Component<CommentProps, {}> {
const avatarDom = (
<div className={`${prefixCls}-avatar`}>
{typeof avatar === 'string' ? <img src={avatar} /> : avatar}
{typeof avatar === 'string' ? <img src={avatar} alt="comment-avatar" /> : avatar}
</div>
);
const actionDom =
actions && actions.length ? (
<ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
<ul className={`${prefixCls}-actions`}>{getAction(actions)}</ul>
) : null;
const authorContent = (

View File

@ -1,3 +1,6 @@
// TODO: remove this lint
// SFC has specified a displayName, but not worked.
/* eslint-disable react/display-name */
import * as React from 'react';
import createReactContext from '@ant-design/create-react-context';

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import Icon from '../icon';
import classNames from 'classnames';
import Icon from '../icon';
export default function InputIcon(props: { suffixIcon: React.ReactNode; prefixCls: string }) {
const { suffixIcon, prefixCls } = props;

View File

@ -30,10 +30,9 @@ function getShowDateFromValue(value: RangePickerValue, mode?: string | string[])
}
if (mode && mode[0] === 'month') {
return [start, end] as RangePickerValue;
} else {
const newEnd = end && end.isSame(start, 'month') ? end.clone().add(1, 'month') : end;
return [start, newEnd] as RangePickerValue;
}
const newEnd = end && end.isSame(start, 'month') ? end.clone().add(1, 'month') : end;
return [start, newEnd] as RangePickerValue;
}
function pickerValueAdapter(
@ -102,7 +101,9 @@ class RangePicker extends React.Component<any, RangePickerState> {
}
private picker: HTMLSpanElement;
private prefixCls?: string;
private tagPrefixCls?: string;
constructor(props: any) {
@ -133,6 +134,17 @@ class RangePicker extends React.Component<any, RangePickerState> {
}
}
setValue(value: RangePickerValue, hidePanel?: boolean) {
this.handleChange(value);
if ((hidePanel || !this.props.showTime) && !('open' in this.props)) {
this.setState({ open: false });
}
}
savePicker = (node: HTMLSpanElement) => {
this.picker = node;
};
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
@ -143,7 +155,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
clearHoverValue = () => this.setState({ hoverValue: [] });
handleChange = (value: RangePickerValue) => {
const props = this.props;
const { props } = this;
if (!('value' in props)) {
this.setState(({ showDate }) => ({
value,
@ -210,13 +222,6 @@ class RangePicker extends React.Component<any, RangePickerState> {
}
};
setValue(value: RangePickerValue, hidePanel?: boolean) {
this.handleChange(value);
if ((hidePanel || !this.props.showTime) && !('open' in this.props)) {
this.setState({ open: false });
}
}
focus() {
this.picker.focus();
}
@ -225,10 +230,6 @@ class RangePicker extends React.Component<any, RangePickerState> {
this.picker.blur();
}
savePicker = (node: HTMLSpanElement) => {
this.picker = node;
};
renderFooter = () => {
const { ranges, renderExtraFooter } = this.props;
const { prefixCls, tagPrefixCls } = this;

View File

@ -39,6 +39,7 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
}
private input: any;
private prefixCls?: string;
constructor(props: any) {
@ -62,6 +63,10 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
}
}
saveInput = (node: any) => {
this.input = node;
};
weekDateRender = (current: any) => {
const selectedValue = this.state.value;
const { prefixCls } = this;
@ -105,13 +110,6 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
this.handleChange(null);
};
renderFooter = (...args: any[]) => {
const { prefixCls, renderExtraFooter } = this.props;
return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
) : null;
};
focus() {
this.input.focus();
}
@ -120,8 +118,11 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
this.input.blur();
}
saveInput = (node: any) => {
this.input = node;
renderFooter = (...args: any[]) => {
const { prefixCls, renderExtraFooter } = this.props;
return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
) : null;
};
renderWeekPicker = ({ getPrefixCls }: ConfigConsumerProps) => {

View File

@ -78,12 +78,8 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
}
}
renderFooter = (...args: any[]) => {
const { renderExtraFooter } = this.props;
const { prefixCls } = this;
return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
) : null;
saveInput = (node: any) => {
this.input = node;
};
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
@ -126,8 +122,12 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
this.input.blur();
}
saveInput = (node: any) => {
this.input = node;
renderFooter = (...args: any[]) => {
const { renderExtraFooter } = this.props;
const { prefixCls } = this;
return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
) : null;
};
renderPicker = ({ getPrefixCls }: ConfigConsumerProps) => {
@ -272,5 +272,6 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
}
}
polyfill(CalenderWrapper);
return CalenderWrapper;
}

View File

@ -89,6 +89,22 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, pickerType
}
}
savePicker = (node: any) => {
this.picker = node;
};
getDefaultLocale = () => {
const result = {
...enUS,
...this.props.locale,
};
result.lang = {
...result.lang,
...(this.props.locale || {}).lang,
};
return result;
};
handleOpenChange = (open: boolean) => {
const { onOpenChange } = this.props;
onOpenChange(open);
@ -130,22 +146,6 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, pickerType
this.picker.blur();
}
savePicker = (node: any) => {
this.picker = node;
};
getDefaultLocale = () => {
const result = {
...enUS,
...this.props.locale,
};
result.lang = {
...result.lang,
...(this.props.locale || {}).lang,
};
return result;
};
renderPicker = (locale: any, localeCode: string) => {
const { format, showTime } = this.props;
const mergedPickerType = showTime ? `${pickerType}Time` : pickerType;

View File

@ -156,13 +156,17 @@ class Descriptions extends React.Component<
size: 'default',
column: defaultColumnMap,
};
static Item: typeof DescriptionsItem = DescriptionsItem;
state: {
screens: BreakpointMap;
} = {
screens: {},
};
token: string;
componentDidMount() {
const { column } = this.props;
this.token = ResponsiveObserve.subscribe(screens => {
@ -189,7 +193,7 @@ class Descriptions extends React.Component<
}
}
}
//If the configuration is not an object, it is a number, return number
// If the configuration is not an object, it is a number, return number
if (typeof column === 'number') {
return column as number;
}

View File

@ -25,7 +25,7 @@ const Divider: React.SFC<DividerProps> = props => (
...restProps
} = props;
const prefixCls = getPrefixCls('divider', customizePrefixCls);
const orientationPrefix = orientation.length > 0 ? '-' + orientation : orientation;
const orientationPrefix = orientation.length > 0 ? `-${orientation}` : orientation;
const classString = classNames(className, prefixCls, `${prefixCls}-${type}`, {
[`${prefixCls}-with-text${orientationPrefix}`]: children,
[`${prefixCls}-dashed`]: !!dashed,

View File

@ -75,7 +75,14 @@ class App extends React.Component {
]}
bordered
renderItem={item => (
<List.Item key={item.id} actions={[<a onClick={this.showDrawer}>View Profile</a>]}>
<List.Item
key={item.id}
actions={[
<a onClick={this.showDrawer} key={`a-${item.id}`}>
View Profile
</a>,
]}
>
<List.Item.Meta
avatar={
<Avatar src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png" />

View File

@ -1,8 +1,9 @@
import * as React from 'react';
import RcDrawer from 'rc-drawer';
import createReactContext from '@ant-design/create-react-context';
import warning from '../_util/warning';
import classNames from 'classnames';
import omit from 'omit.js';
import warning from '../_util/warning';
import Icon from '../icon';
import { withConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { tuple } from '../_util/type';
@ -159,6 +160,7 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
const { closable, prefixCls, onClose } = this.props;
return (
closable && (
// eslint-disable-next-line react/button-has-type
<button onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
<Icon type="close" />
</button>
@ -208,27 +210,12 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
renderProvider = (value: Drawer) => {
const {
prefixCls,
zIndex,
style,
placement,
className,
wrapClassName,
width,
height,
closable,
destroyOnClose,
mask,
bodyStyle,
title,
push,
visible,
// ConfigConsumerProps
getPopupContainer,
rootPrefixCls,
getPrefixCls,
renderEmpty,
csp,
autoInsertSpaceInButton,
...rest
} = this.props;
warning(
@ -248,7 +235,22 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
<DrawerContext.Provider value={this}>
<RcDrawer
handler={false}
{...rest}
{...omit(rest, [
'zIndex',
'style',
'closable',
'destroyOnClose',
'bodyStyle',
'title',
'push',
'visible',
'getPopupContainer',
'rootPrefixCls',
'getPrefixCls',
'renderEmpty',
'csp',
'autoInsertSpaceInButton',
])}
{...offsetStyle}
prefixCls={prefixCls}
open={this.props.visible}

View File

@ -54,6 +54,7 @@ export interface DropDownProps {
export default class Dropdown extends React.Component<DropDownProps, any> {
static Button: typeof DropdownButton;
static defaultProps = {
mouseEnterDelay: 0.15,
mouseLeaveDelay: 0.1,
@ -90,7 +91,7 @@ export default class Dropdown extends React.Component<DropDownProps, any> {
warning(
!overlayProps.mode || overlayProps.mode === 'vertical',
'Dropdown',
`mode="${overlayProps.mode}" is not supported for Dropdown\'s Menu.`,
`mode="${overlayProps.mode}" is not supported for Dropdown's Menu.`,
);
// menu cannot be selectable in dropdown defaultly

View File

@ -475,6 +475,7 @@ exports[`renders ./components/empty/demo/config-provider.md correctly 1`] = `
>
<div
class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left"
style="margin-top:8px"
>
<div
class="ant-table-content"

View File

@ -1,6 +1,6 @@
import * as React from 'react';
export default () => (
const Empty = () => (
<svg width="184" height="152" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<g transform="translate(24 31.67)">
@ -34,3 +34,5 @@ export default () => (
</g>
</svg>
);
export default Empty;

View File

@ -1,6 +1,6 @@
import * as React from 'react';
export default () => (
const Simple = () => (
<svg width="64" height="41" viewBox="0 0 64 41" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 1)" fill="none" fillRule="evenodd">
<ellipse fill="#F5F5F5" cx="32" cy="33" rx="32" ry="7" />
@ -14,3 +14,5 @@ export default () => (
</g>
</svg>
);
export default Simple;

View File

@ -91,7 +91,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
} else if (React.isValidElement(e.message)) {
node = e.message;
}
// eslint-disable-next-line react/no-array-index-key
return node ? React.cloneElement(node, { key: index }) : e.message;
}),
);
@ -151,6 +151,40 @@ export default class FormItem extends React.Component<FormItemProps, any> {
return this.getChildProp(FIELD_DATA_PROP);
}
getValidateStatus() {
const onlyControl = this.getOnlyControl();
if (!onlyControl) {
return '';
}
const field = this.getField();
if (field.validating) {
return 'validating';
}
if (field.errors) {
return 'error';
}
const fieldValue = 'value' in field ? field.value : this.getMeta().initialValue;
if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
return 'success';
}
return '';
}
// Resolve duplicated ids bug between different forms
// https://github.com/ant-design/ant-design/issues/7351
onLabelClick = () => {
const id = this.props.id || this.getId();
if (!id) {
return;
}
const formItemNode = ReactDOM.findDOMNode(this) as Element;
const control = formItemNode.querySelector(`[id="${id}"]`) as HTMLElement;
if (control && control.focus) {
control.focus();
}
};
onHelpAnimEnd = (_key: string, helpShow: boolean) => {
this.helpShow = helpShow;
if (!helpShow) {
@ -158,6 +192,24 @@ export default class FormItem extends React.Component<FormItemProps, any> {
}
};
isRequired() {
const { required } = this.props;
if (required !== undefined) {
return required;
}
if (this.getOnlyControl()) {
const meta = this.getMeta() || {};
const validate = meta.validate || [];
return validate
.filter((item: any) => !!item.rules)
.some((item: any) => {
return item.rules.some((rule: any) => rule.required);
});
}
return false;
}
renderHelp(prefixCls: string) {
const help = this.getHelpMessage();
const children = help ? (
@ -186,25 +238,6 @@ export default class FormItem extends React.Component<FormItemProps, any> {
return extra ? <div className={`${prefixCls}-extra`}>{extra}</div> : null;
}
getValidateStatus() {
const onlyControl = this.getOnlyControl();
if (!onlyControl) {
return '';
}
const field = this.getField();
if (field.validating) {
return 'validating';
}
if (field.errors) {
return 'error';
}
const fieldValue = 'value' in field ? field.value : this.getMeta().initialValue;
if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
return 'success';
}
return '';
}
renderValidateWrapper(
prefixCls: string,
c1: React.ReactNode,
@ -293,39 +326,6 @@ export default class FormItem extends React.Component<FormItemProps, any> {
);
}
isRequired() {
const { required } = this.props;
if (required !== undefined) {
return required;
}
if (this.getOnlyControl()) {
const meta = this.getMeta() || {};
const validate = meta.validate || [];
return validate
.filter((item: any) => !!item.rules)
.some((item: any) => {
return item.rules.some((rule: any) => rule.required);
});
}
return false;
}
// Resolve duplicated ids bug between different forms
// https://github.com/ant-design/ant-design/issues/7351
onLabelClick = () => {
const id = this.props.id || this.getId();
if (!id) {
return;
}
const formItemNode = ReactDOM.findDOMNode(this) as Element;
const control = formItemNode.querySelector(`[id="${id}"]`) as HTMLElement;
if (control && control.focus) {
control.focus();
}
};
renderLabel(prefixCls: string) {
return (
<FormContext.Consumer key="label">

View File

@ -81,7 +81,7 @@ describe('Form', () => {
myForm.setFields({
account: {
errors: [<div>Error 1</div>, <div>Error 2</div>],
errors: [<div key="error-1">Error 1</div>, <div key="error-2">Error 2</div>],
},
});

View File

@ -53,7 +53,7 @@ class PriceInput extends React.Component {
handleNumberChange = e => {
const number = parseInt(e.target.value || 0, 10);
if (Number.isNaN(number)) {
if (isNaN(number)) {
return;
}
if (!('value' in this.props)) {

View File

@ -50,7 +50,7 @@ export default class Col extends React.Component<ColProps, {}> {
};
renderCol = ({ getPrefixCls }: ConfigConsumerProps) => {
const props: any = this.props;
const { props } = this;
const {
prefixCls: customizePrefixCls,
span,
@ -66,13 +66,14 @@ export default class Col extends React.Component<ColProps, {}> {
let sizeClassObj = {};
['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].forEach(size => {
let sizeProps: ColSize = {};
if (typeof props[size] === 'number') {
sizeProps.span = props[size];
} else if (typeof props[size] === 'object') {
sizeProps = props[size] || {};
const propSize = (props as any)[size];
if (typeof propSize === 'number') {
sizeProps.span = propSize;
} else if (typeof propSize === 'object') {
sizeProps = propSize || {};
}
delete others[size];
delete (others as any)[size];
sizeClassObj = {
...sizeClassObj,
@ -100,7 +101,7 @@ export default class Col extends React.Component<ColProps, {}> {
return (
<RowContext.Consumer>
{({ gutter }) => {
let style = others.style;
let { style } = others;
if (gutter! > 0) {
style = {
paddingLeft: gutter! / 2,

View File

@ -1,7 +1,7 @@
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import * as React from 'react';
import classNames from 'classnames';
import * as PropTypes from 'prop-types';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import RowContext from './RowContext';
import { tuple } from '../_util/type';
import ResponsiveObserve, {
@ -42,7 +42,9 @@ export default class Row extends React.Component<RowProps, RowState> {
state: RowState = {
screens: {},
};
token: string;
componentDidMount() {
this.token = ResponsiveObserve.subscribe(screens => {
if (typeof this.props.gutter === 'object') {
@ -50,9 +52,11 @@ export default class Row extends React.Component<RowProps, RowState> {
}
});
}
componentWillUnmount() {
ResponsiveObserve.unsubscribe(this.token);
}
getGutter(): number | undefined {
const { gutter } = this.props;
if (typeof gutter === 'object') {
@ -65,6 +69,7 @@ export default class Row extends React.Component<RowProps, RowState> {
}
return gutter as number;
}
renderRow = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,

View File

@ -61,9 +61,9 @@
}
.@{ant-prefix}-col {
position: relative;
// Prevent columns from collapsing when empty
min-height: 1px;
position: relative;
}
.make-grid-columns();

View File

@ -1,5 +1,5 @@
import Icon, { IconProps } from './index';
import * as React from 'react';
import Icon, { IconProps } from './index';
const customCache = new Set<string>();

View File

@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import * as React from 'react';
import classNames from 'classnames';
import * as allIcons from '@ant-design/icons/lib/dist';
@ -18,7 +19,27 @@ import { getTwoToneColor, setTwoToneColor } from './twoTonePrimaryColor';
ReactIcon.add(...Object.keys(allIcons).map(key => (allIcons as any)[key]));
setTwoToneColor('#1890ff');
let defaultTheme: ThemeType = 'outlined';
let dangerousTheme: ThemeType | undefined = undefined;
let dangerousTheme: ThemeType | undefined;
function unstable_ChangeThemeOfIconsDangerously(theme?: ThemeType) {
warning(
false,
'Icon',
`You are using the unstable method 'Icon.unstable_ChangeThemeOfAllIconsDangerously', ` +
`make sure that all the icons with theme '${theme}' display correctly.`,
);
dangerousTheme = theme;
}
function unstable_ChangeDefaultThemeOfIcons(theme: ThemeType) {
warning(
false,
'Icon',
`You are using the unstable method 'Icon.unstable_ChangeDefaultThemeOfIcons', ` +
`make sure that all the icons with theme '${theme}' display correctly.`,
);
defaultTheme = theme;
}
export interface TransferLocale {
icon: string;
@ -196,26 +217,6 @@ const Icon: IconComponent<IconProps> = props => {
);
};
function unstable_ChangeThemeOfIconsDangerously(theme?: ThemeType) {
warning(
false,
'Icon',
`You are using the unstable method 'Icon.unstable_ChangeThemeOfAllIconsDangerously', ` +
`make sure that all the icons with theme '${theme}' display correctly.`,
);
dangerousTheme = theme;
}
function unstable_ChangeDefaultThemeOfIcons(theme: ThemeType) {
warning(
false,
'Icon',
`You are using the unstable method 'Icon.unstable_ChangeDefaultThemeOfIcons', ` +
`make sure that all the icons with theme '${theme}' display correctly.`,
);
defaultTheme = theme;
}
Icon.createFromIconfontCN = createFromIconfontCN;
Icon.getTwoToneColor = getTwoToneColor;
Icon.setTwoToneColor = setTwoToneColor;

View File

@ -39,8 +39,11 @@ export interface InputProps
class Input extends React.Component<InputProps, any> {
static Group: typeof Group;
static Search: typeof Search;
static TextArea: typeof TextArea;
static Password: typeof Password;
static defaultProps = {
@ -88,6 +91,10 @@ class Input extends React.Component<InputProps, any> {
};
}
// Since polyfill `getSnapshotBeforeUpdate` need work with `componentDidUpdate`.
// We keep an empty function here.
componentDidUpdate() {}
getSnapshotBeforeUpdate(prevProps: InputProps) {
if (hasPrefixSuffix(prevProps) !== hasPrefixSuffix(this.props)) {
warning(
@ -99,32 +106,6 @@ class Input extends React.Component<InputProps, any> {
return null;
}
// Since polyfill `getSnapshotBeforeUpdate` need work with `componentDidUpdate`.
// We keep an empty function here.
componentDidUpdate() {}
handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const { onPressEnter, onKeyDown } = this.props;
if (e.keyCode === 13 && onPressEnter) {
onPressEnter(e);
}
if (onKeyDown) {
onKeyDown(e);
}
};
focus() {
this.input.focus();
}
blur() {
this.input.blur();
}
select() {
this.input.select();
}
getInputClassName(prefixCls: string) {
const { size, disabled } = this.props;
return classNames(prefixCls, {
@ -134,10 +115,6 @@ class Input extends React.Component<InputProps, any> {
});
}
saveInput = (node: HTMLInputElement) => {
this.input = node;
};
setValue(
value: string,
e: React.ChangeEvent<HTMLInputElement> | React.MouseEvent<HTMLElement, MouseEvent>,
@ -166,6 +143,20 @@ class Input extends React.Component<InputProps, any> {
}
}
saveInput = (node: HTMLInputElement) => {
this.input = node;
};
handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const { onPressEnter, onKeyDown } = this.props;
if (e.keyCode === 13 && onPressEnter) {
onPressEnter(e);
}
if (onKeyDown) {
onKeyDown(e);
}
};
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
this.setValue('', e, () => {
this.focus();
@ -176,6 +167,18 @@ class Input extends React.Component<InputProps, any> {
this.setValue(e.target.value, e);
};
focus() {
this.input.focus();
}
blur() {
this.input.blur();
}
select() {
this.input.select();
}
renderClearIcon(prefixCls: string) {
const { allowClear } = this.props;
const { value } = this.state;

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import classNames from 'classnames';
import omit from 'omit.js';
import Input, { InputProps } from './Input';
import Icon from '../icon';
@ -31,9 +32,7 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
};
onChange = () => {
this.setState({
visible: !this.state.visible,
});
this.setState(({ visible }) => ({ visible: !visible }));
};
getIcon() {
@ -59,7 +58,6 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
prefixCls,
inputPrefixCls,
size,
suffix,
visibilityToggle,
...restProps
} = this.props;
@ -69,7 +67,7 @@ export default class Password extends React.Component<PasswordProps, PasswordSta
});
return (
<Input
{...restProps}
{...omit(restProps, ['suffix'])}
type={this.state.visible ? 'text' : 'password'}
size={size}
className={inputClassName}

View File

@ -21,6 +21,10 @@ export default class Search extends React.Component<SearchProps, any> {
private input: Input;
saveInput = (node: Input) => {
this.input = node;
};
onSearch = (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLInputElement>) => {
const { onSearch } = this.props;
if (onSearch) {
@ -37,10 +41,6 @@ export default class Search extends React.Component<SearchProps, any> {
this.input.blur();
}
saveInput = (node: Input) => {
this.input = node;
};
renderSuffix = (prefixCls: string) => {
const { suffix, enterButton } = this.props;
if (enterButton) return suffix;

View File

@ -28,6 +28,7 @@ export interface TextAreaState {
class TextArea extends React.Component<TextAreaProps, TextAreaState> {
nextFrameActionId: number;
resizeFrameId: number;
state = {
@ -53,32 +54,8 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
raf.cancel(this.resizeFrameId);
}
resizeOnNextFrame = () => {
raf.cancel(this.nextFrameActionId);
this.nextFrameActionId = raf(this.resizeTextarea);
};
focus() {
this.textAreaRef.focus();
}
blur() {
this.textAreaRef.blur();
}
resizeTextarea = () => {
const { autosize } = this.props;
if (!autosize || !this.textAreaRef) {
return;
}
const { minRows, maxRows } = autosize as AutoSizeType;
const textareaStyles = calculateNodeHeight(this.textAreaRef, false, minRows, maxRows);
this.setState({ textareaStyles, resizing: true }, () => {
raf.cancel(this.resizeFrameId);
this.resizeFrameId = raf(() => {
this.setState({ resizing: false });
});
});
saveTextAreaRef = (textArea: HTMLTextAreaElement) => {
this.textAreaRef = textArea;
};
handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
@ -101,10 +78,34 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
}
};
saveTextAreaRef = (textArea: HTMLTextAreaElement) => {
this.textAreaRef = textArea;
resizeOnNextFrame = () => {
raf.cancel(this.nextFrameActionId);
this.nextFrameActionId = raf(this.resizeTextarea);
};
resizeTextarea = () => {
const { autosize } = this.props;
if (!autosize || !this.textAreaRef) {
return;
}
const { minRows, maxRows } = autosize as AutoSizeType;
const textareaStyles = calculateNodeHeight(this.textAreaRef, false, minRows, maxRows);
this.setState({ textareaStyles, resizing: true }, () => {
raf.cancel(this.resizeFrameId);
this.resizeFrameId = raf(() => {
this.setState({ resizing: false });
});
});
};
focus() {
this.textAreaRef.focus();
}
blur() {
this.textAreaRef.blur();
}
renderTextArea = ({ getPrefixCls }: ConfigConsumerProps) => {
const { textareaStyles, resizing } = this.state;
const { prefixCls: customizePrefixCls, className, disabled, autosize } = this.props;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
/* eslint-disable import/no-unresolved */
// eslint-disable-next-line import/no-unresolved
import Form from '../../form';
import Input from '..';
import focusTest from '../../../tests/shared/focusTest';

View File

@ -124,10 +124,10 @@ export default function calculateNodeHeight(
if (boxSizing === 'border-box') {
// border-box: add border, since height = content + padding + border
height = height + borderSize;
height += borderSize;
} else if (boxSizing === 'content-box') {
// remove padding, since height = content
height = height - paddingSize;
height -= paddingSize;
}
if (minRows !== null || maxRows !== null) {

View File

@ -36,7 +36,7 @@ class NumericInput extends React.Component {
onChange = e => {
const { value } = e.target;
const reg = /^-?(0|[1-9][0-9]*)(\.[0-9]*)?$/;
if ((!Number.isNaN(value) && reg.test(value)) || value === '' || value === '-') {
if ((!isNaN(value) && reg.test(value)) || value === '' || value === '-') {
this.props.onChange(value);
}
};

View File

@ -40,6 +40,7 @@
background-color: @input-disabled-bg;
cursor: not-allowed;
opacity: 1;
&:hover {
.hover(@input-border-color);
}
@ -116,6 +117,7 @@
> [class*='col-'] {
padding-right: 8px;
&:last-child {
padding-right: 0;
}
@ -147,10 +149,12 @@
width: 100%;
margin-bottom: 0;
text-align: inherit;
&:focus {
z-index: 1; // Fix https://gw.alipayobjects.com/zos/rmsportal/DHNpoqfMXSfrSnlZvhsJ.png
border-right-width: 1px;
}
&:hover {
z-index: 1;
border-right-width: 1px;
@ -229,6 +233,7 @@
&-addon:first-child {
border-right: 0;
}
&-addon:last-child {
border-left: 0;
}
@ -280,9 +285,11 @@
> .@{inputClass} {
&:not(:first-child):not(:last-child) {
border-right-width: @border-width-base;
&:hover {
z-index: 1;
}
&:focus {
z-index: 1;
}
@ -315,9 +322,11 @@
& > .@{ant-prefix}-time-picker .@{ant-prefix}-time-picker-input {
border-right-width: @border-width-base;
border-radius: 0;
&:hover {
z-index: 1;
}
&:focus {
z-index: 1;
}
@ -383,6 +392,7 @@
color: @input-color;
line-height: 0;
transform: translateY(-50%);
:not(.anticon) {
line-height: @line-height-base;
}
@ -403,6 +413,7 @@
.@{inputClass}:not(:last-child) {
padding-right: @input-padding-horizontal-base + @input-affix-width;
}
&.@{inputClass}-affix-wrapper-with-clear-btn .@{inputClass}:not(:last-child) {
padding-right: @input-padding-horizontal-base + @input-affix-with-clear-btn-width;
}

View File

@ -1,6 +1,13 @@
import createContext, { Context } from '@ant-design/create-react-context';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import * as React from 'react';
import { polyfill } from 'react-lifecycles-compat';
import classNames from 'classnames';
import omit from 'omit.js';
import { LayoutContext, LayoutContextProps } from './layout';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import Icon from '../icon';
import isNumeric from '../_util/isNumeric';
// matchMedia polyfill for
// https://github.com/WickyNilliams/enquire.js/issues/82
@ -16,13 +23,6 @@ if (typeof window !== 'undefined') {
window.matchMedia = window.matchMedia || matchMediaPolyfill;
}
import * as React from 'react';
import { polyfill } from 'react-lifecycles-compat';
import classNames from 'classnames';
import omit from 'omit.js';
import Icon from '../icon';
import isNumeric from '../_util/isNumeric';
const dimensionMap = {
xs: '480px',
sm: '576px',
@ -95,6 +95,7 @@ class InternalSider extends React.Component<InternalSideProps, SiderState> {
}
private mql: MediaQueryList;
private uniqueId: string;
constructor(props: InternalSideProps) {
@ -169,7 +170,7 @@ class InternalSider extends React.Component<InternalSideProps, SiderState> {
};
belowShowChange = () => {
this.setState({ belowShow: !this.state.belowShow });
this.setState(({ belowShow }) => ({ belowShow: !belowShow }));
};
renderSider = ({ getPrefixCls }: ConfigConsumerProps) => {
@ -266,6 +267,7 @@ class InternalSider extends React.Component<InternalSideProps, SiderState> {
polyfill(InternalSider);
// eslint-disable-next-line react/prefer-stateless-function
export default class Sider extends React.Component {
render() {
return (

View File

@ -31,11 +31,14 @@ interface BasicPropsWithTagName extends BasicProps {
}
function generator({ suffixCls, tagName }: GeneratorProps) {
return (BasicComponent: React.ComponentClass<BasicPropsWithTagName>): any => {
return (BasicComponent: any) => {
return class Adapter extends React.Component<BasicProps, any> {
static Header: any;
static Footer: any;
static Content: any;
static Sider: any;
renderComponent = ({ getPrefixCls }: ConfigConsumerProps) => {
@ -52,13 +55,11 @@ function generator({ suffixCls, tagName }: GeneratorProps) {
};
}
class Basic extends React.Component<BasicPropsWithTagName, any> {
render() {
const { prefixCls, className, children, tagName, ...others } = this.props;
const classString = classNames(className, prefixCls);
return React.createElement(tagName, { className: classString, ...others }, children);
}
}
const Basic = (props: BasicPropsWithTagName) => {
const { prefixCls, className, children, tagName, ...others } = props;
const classString = classNames(className, prefixCls);
return React.createElement(tagName, { className: classString, ...others }, children);
};
interface BasicLayoutState {
siders: string[];

View File

@ -106,6 +106,7 @@ export default class Item extends React.Component<ListItemProps, any> {
const actionsContent = actions && actions.length > 0 && (
<ul className={`${prefixCls}-item-action`} key="actions">
{actions.map((action: React.ReactNode, i: number) => (
// eslint-disable-next-line react/no-array-index-key
<li key={`${prefixCls}-item-action-${i}`}>
{action}
{i !== actions.length - 1 && <em className={`${prefixCls}-item-action-split`} />}

View File

@ -109,7 +109,11 @@ describe('List Item Layout', () => {
<List
dataSource={data}
renderItem={item => (
<List.Item key={item.title} actions={[<a>Action</a>]} extra={<span>{item.extra}</span>}>
<List.Item
key={item.title}
actions={[<a key="action">Action</a>]}
extra={<span>{item.extra}</span>}
>
<List.Item.Meta
title={<a href={item.href}>{item.title}</a>}
description={item.description}

View File

@ -98,7 +98,9 @@ class LoadMoreList extends React.Component {
loadMore={loadMore}
dataSource={list}
renderItem={item => (
<List.Item actions={[<a>edit</a>, <a>more</a>]}>
<List.Item
actions={[<a key="list-loadmore-edit">edit</a>, <a key="list-loadmore-more">more</a>]}
>
<Skeleton avatar title={false} loading={item.loading} active>
<List.Item.Meta
avatar={

View File

@ -56,9 +56,9 @@ ReactDOM.render(
<List.Item
key={item.title}
actions={[
<IconText type="star-o" text="156" />,
<IconText type="like-o" text="156" />,
<IconText type="message" text="2" />,
<IconText type="star-o" text="156" key="list-vertical-star-o" />,
<IconText type="like-o" text="156" key="list-vertical-like-o" />,
<IconText type="message" text="2" key="list-vertical-message" />,
]}
extra={
<img

View File

@ -1,10 +1,10 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import { SpinProps } from '../spin';
import omit from 'omit.js';
import Spin, { SpinProps } from '../spin';
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
import Spin from '../spin';
import Pagination, { PaginationConfig } from '../pagination';
import { Row } from '../grid';
@ -174,12 +174,9 @@ export default class List<T> extends React.Component<ListProps<T>, ListState> {
grid,
dataSource = [],
size,
rowKey,
renderItem,
header,
footer,
loading,
locale,
...rest
} = this.props;
@ -201,6 +198,7 @@ export default class List<T> extends React.Component<ListProps<T>, ListState> {
break;
case 'small':
sizeCls = 'sm';
break;
default:
break;
}
@ -273,7 +271,7 @@ export default class List<T> extends React.Component<ListProps<T>, ListState> {
const paginationPosition = paginationProps.position || 'bottom';
return (
<div className={classString} {...rest}>
<div className={classString} {...omit(rest, ['rowKey', 'renderItem', 'locale'])}>
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
{header && <div className={`${prefixCls}-header`}>{header}</div>}
<Spin {...loadingProp}>

View File

@ -118,7 +118,9 @@ describe('Mention', () => {
if (process.env.REACT === '15') {
return;
}
const wrapper = mount(<Mention defaultSuggestions={[<Mention.Nav value="light" />]} />);
const wrapper = mount(
<Mention defaultSuggestions={[<Mention.Nav key="light" value="light" />]} />,
);
wrapper.find('DraftEditorContents').simulate('focus');
const ed = wrapper.find('.public-DraftEditor-content');
ed.simulate('beforeInput', { data: '@l' });

View File

@ -54,7 +54,12 @@ class CustomNavMention extends React.Component {
item => item.name.toLowerCase().indexOf(searchValue) !== -1,
);
const suggestions = filtered.map(suggestion => (
<Nav value={suggestion.name} data={suggestion} disabled={suggestion.disabled}>
<Nav
value={suggestion.name}
data={suggestion}
disabled={suggestion.disabled}
key="mention-avatar"
>
<Avatar
src={suggestion.icon}
size="small"

View File

@ -43,7 +43,7 @@ class CustomNavMention extends React.Component {
item => item.name.toLowerCase().indexOf(searchValue) !== -1,
);
const suggestions = filtered.map(suggestion => (
<Nav value={suggestion.name} data={suggestion}>
<Nav value={suggestion.name} data={suggestion} key={suggestion.name}>
<span>
{suggestion.name} - {suggestion.type}
</span>

View File

@ -42,17 +42,22 @@ export interface MentionState {
class Mention extends React.Component<MentionProps, MentionState> {
static getMentions = getMentions;
static defaultProps = {
notFoundContent: 'No matches found',
loading: false,
multiLines: false,
placement: 'bottom' as MentionPlacement,
};
static Nav = Nav;
static toString = toString;
static toContentState = toEditorState;
private mentionEle: any;
constructor(props: MentionProps) {
super(props);
this.state = {
@ -67,6 +72,10 @@ class Mention extends React.Component<MentionProps, MentionState> {
);
}
mentionRef = (ele: any) => {
this.mentionEle = ele;
};
onSearchChange = (value: string, prefix: string) => {
if (this.props.onSearchChange) {
return this.props.onSearchChange(value, prefix);
@ -80,22 +89,6 @@ class Mention extends React.Component<MentionProps, MentionState> {
}
};
defaultSearchChange(value: string): void {
const searchValue = value.toLowerCase();
const filteredSuggestions = (this.props.defaultSuggestions || []).filter(suggestion => {
if (typeof suggestion === 'string') {
return suggestion.toLowerCase().indexOf(searchValue) !== -1;
} else if (suggestion.type && suggestion.type === Nav) {
return suggestion.props.value
? suggestion.props.value.toLowerCase().indexOf(searchValue) !== -1
: true;
}
});
this.setState({
filteredSuggestions,
});
}
onFocus = (ev: React.FocusEvent<HTMLElement>) => {
this.setState({
focus: true,
@ -118,9 +111,24 @@ class Mention extends React.Component<MentionProps, MentionState> {
this.mentionEle._editor.focusEditor();
};
mentionRef = (ele: any) => {
this.mentionEle = ele;
};
defaultSearchChange(value: string): void {
const searchValue = value.toLowerCase();
const filteredSuggestions = (this.props.defaultSuggestions || []).filter(suggestion => {
if (typeof suggestion === 'string') {
return suggestion.toLowerCase().indexOf(searchValue) !== -1;
}
if (suggestion.type && suggestion.type === Nav) {
return suggestion.props.value
? suggestion.props.value.toLowerCase().indexOf(searchValue) !== -1
: true;
}
return false;
});
this.setState({
filteredSuggestions,
});
}
renderMention = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,

View File

@ -4,11 +4,10 @@ import * as React from 'react';
import { polyfill } from 'react-lifecycles-compat';
import RcMentions from 'rc-mentions';
import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
import { OptionProps as RcOptionProps } from 'rc-mentions/lib/Option';
import Spin from '../spin';
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
const Option: React.FunctionComponent<RcOptionProps> = RcMentions.Option;
const { Option } = RcMentions;
function loadingFilterOption() {
return true;
@ -109,7 +108,7 @@ class Mentions extends React.Component<MentionProps, MentionState> {
const { children, loading } = this.props;
if (loading) {
return (
<Option value={'ANTD_SEARCHING'} disabled>
<Option value="ANTD_SEARCHING" disabled>
<Spin size="small" />
</Option>
);

View File

@ -24,6 +24,7 @@ export interface MenuItemProps
export default class MenuItem extends React.Component<MenuItemProps> {
static isMenuItem = true;
private menuItem: this;
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {

View File

@ -26,8 +26,10 @@ class SubMenu extends React.Component<SubMenuProps, any> {
static contextTypes = {
antdMenuTheme: PropTypes.string,
};
// fix issue:https://github.com/ant-design/ant-design/issues/8666
static isSubMenu = 1;
private subMenu: any;
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {

View File

@ -2,12 +2,12 @@ import * as React from 'react';
import RcMenu, { Divider, ItemGroup } from 'rc-menu';
import classNames from 'classnames';
import omit from 'omit.js';
import { polyfill } from 'react-lifecycles-compat';
import SubMenu from './SubMenu';
import Item from './MenuItem';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import animation from '../_util/openAnimation';
import warning from '../_util/warning';
import { polyfill } from 'react-lifecycles-compat';
import { SiderContext, SiderContextProps } from '../layout/Sider';
import raf from '../_util/raf';
import MenuContext, { MenuTheme } from './MenuContext';
@ -169,15 +169,46 @@ class InternalMenu extends React.Component<InternalMenuProps, MenuState> {
raf.cancel(this.mountRafId);
}
restoreModeVerticalFromInline() {
const { switchingModeFromInline } = this.state;
if (switchingModeFromInline) {
this.setState({
switchingModeFromInline: false,
});
setOpenKeys(openKeys: string[]) {
if (!('openKeys' in this.props)) {
this.setState({ openKeys });
}
}
getRealMenuMode() {
const inlineCollapsed = this.getInlineCollapsed();
if (this.state.switchingModeFromInline && inlineCollapsed) {
return 'inline';
}
const { mode } = this.props;
return inlineCollapsed ? 'vertical' : mode;
}
getInlineCollapsed() {
const { inlineCollapsed } = this.props;
if (this.props.siderCollapsed !== undefined) {
return this.props.siderCollapsed;
}
return inlineCollapsed;
}
getMenuOpenAnimation(menuMode: MenuMode) {
const { openAnimation, openTransitionName } = this.props;
let menuOpenAnimation = openAnimation || openTransitionName;
if (openAnimation === undefined && openTransitionName === undefined) {
if (menuMode === 'horizontal') {
menuOpenAnimation = 'slide-up';
} else if (menuMode === 'inline') {
menuOpenAnimation = animation;
} else {
// When mode switch from inline
// submenu should hide without animation
menuOpenAnimation = this.state.switchingModeFromInline ? '' : 'zoom-big';
}
}
return menuOpenAnimation;
}
// Restore vertical mode when menu is collapsed responsively when mounted
// https://github.com/ant-design/ant-design/issues/13104
// TODO: not a perfect solution, looking a new way to avoid setting switchingModeFromInline in this situation
@ -229,50 +260,15 @@ class InternalMenu extends React.Component<InternalMenuProps, MenuState> {
}
};
setOpenKeys(openKeys: string[]) {
if (!('openKeys' in this.props)) {
this.setState({ openKeys });
restoreModeVerticalFromInline() {
const { switchingModeFromInline } = this.state;
if (switchingModeFromInline) {
this.setState({
switchingModeFromInline: false,
});
}
}
getRealMenuMode() {
const inlineCollapsed = this.getInlineCollapsed();
if (this.state.switchingModeFromInline && inlineCollapsed) {
return 'inline';
}
const { mode } = this.props;
return inlineCollapsed ? 'vertical' : mode;
}
getInlineCollapsed() {
const { inlineCollapsed } = this.props;
if (this.props.siderCollapsed !== undefined) {
return this.props.siderCollapsed;
}
return inlineCollapsed;
}
getMenuOpenAnimation(menuMode: MenuMode) {
const { openAnimation, openTransitionName } = this.props;
let menuOpenAnimation = openAnimation || openTransitionName;
if (openAnimation === undefined && openTransitionName === undefined) {
if (menuMode === 'horizontal') {
menuOpenAnimation = 'slide-up';
} else if (menuMode === 'inline') {
menuOpenAnimation = animation;
} else {
// When mode switch from inline
// submenu should hide without animation
if (this.state.switchingModeFromInline) {
menuOpenAnimation = '';
} else {
menuOpenAnimation = 'zoom-big';
}
}
}
return menuOpenAnimation;
}
renderMenu = ({ getPopupContainer, getPrefixCls }: ConfigConsumerProps) => {
const { mounted } = this.state;
const { prefixCls: customizePrefixCls, className, theme, collapsedWidth } = this.props;
@ -339,8 +335,11 @@ polyfill(InternalMenu);
// We should keep this as ref-able
export default class Menu extends React.Component<MenuProps, {}> {
static Divider = Divider;
static Item = Item;
static SubMenu = SubMenu;
static ItemGroup = ItemGroup;
render() {

View File

@ -1,4 +1,3 @@
/* global Promise */
import * as React from 'react';
import Notification from 'rc-notification';
import Icon from '../icon';
@ -78,6 +77,7 @@ function notice(args: ArgsProps): MessageType {
const iconNode = (
<Icon type={iconType} theme={iconType === 'loading' ? 'outlined' : 'filled'} />
);
const switchIconNode = iconType ? iconNode : '';
instance.notice({
key: target,
duration,
@ -88,7 +88,7 @@ function notice(args: ArgsProps): MessageType {
args.type ? ` ${prefixCls}-${args.type}` : ''
}`}
>
{args.icon ? args.icon : iconType ? iconNode : ''}
{args.icon ? args.icon : switchIconNode}
<span>{args.content}</span>
</div>
),
@ -159,7 +159,7 @@ const api: any = {
onClose = duration;
duration = undefined;
}
return api.open({ content, duration: duration, type, onClose });
return api.open({ content, duration, type, onClose });
};
});

View File

@ -71,7 +71,7 @@ export default class ActionButton extends React.Component<ActionButtonProps, Act
render() {
const { type, children, buttonProps } = this.props;
const loading = this.state.loading;
const { loading } = this.state;
return (
<Button type={type} onClick={this.onClick} loading={loading} {...buttonProps}>
{children}

View File

@ -31,34 +31,34 @@ if (typeof window !== 'undefined' && window.document && window.document.document
}
export interface ModalProps {
/** 对话框是否可见*/
/** 对话框是否可见 */
visible?: boolean;
/** 确定按钮 loading*/
/** 确定按钮 loading */
confirmLoading?: boolean;
/** 标题*/
/** 标题 */
title?: React.ReactNode | string;
/** 是否显示右上角的关闭按钮*/
/** 是否显示右上角的关闭按钮 */
closable?: boolean;
/** 点击确定回调*/
/** 点击确定回调 */
onOk?: (e: React.MouseEvent<HTMLElement>) => void;
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调 */
onCancel?: (e: React.MouseEvent<HTMLElement>) => void;
afterClose?: () => void;
/** 垂直居中 */
centered?: boolean;
/** 宽度*/
/** 宽度 */
width?: string | number;
/** 底部内容*/
/** 底部内容 */
footer?: React.ReactNode;
/** 确认按钮文字*/
/** 确认按钮文字 */
okText?: React.ReactNode;
/** 确认按钮类型*/
/** 确认按钮类型 */
okType?: ButtonType;
/** 取消按钮文字*/
/** 取消按钮文字 */
cancelText?: React.ReactNode;
/** 点击蒙层是否允许关闭*/
/** 点击蒙层是否允许关闭 */
maskClosable?: boolean;
/** 强制渲染 Modal*/
/** 强制渲染 Modal */
forceRender?: boolean;
okButtonProps?: NativeButtonProps;
cancelButtonProps?: NativeButtonProps;
@ -129,11 +129,17 @@ export interface ModalLocale {
export default class Modal extends React.Component<ModalProps, {}> {
static info: ModalFunc;
static success: ModalFunc;
static error: ModalFunc;
static warn: ModalFunc;
static warning: ModalFunc;
static confirm: ModalFunc;
static destroyAll: () => void;
static defaultProps = {
@ -161,14 +167,14 @@ export default class Modal extends React.Component<ModalProps, {}> {
};
handleCancel = (e: React.MouseEvent<HTMLButtonElement>) => {
const onCancel = this.props.onCancel;
const { onCancel } = this.props;
if (onCancel) {
onCancel(e);
}
};
handleOk = (e: React.MouseEvent<HTMLButtonElement>) => {
const onOk = this.props.onOk;
const { onOk } = this.props;
if (onOk) {
onOk(e);
}

View File

@ -80,7 +80,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
prefixCls={prefixCls}
className={classString}
wrapClassName={classNames({ [`${contentPrefixCls}-centered`]: !!props.centered })}
onCancel={close.bind(this, { triggerCancel: true })}
onCancel={() => close({ triggerCancel: true })}
visible={visible}
title=""
transitionName={transitionName}
@ -123,8 +123,32 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
export default function confirm(config: ModalFuncProps) {
const div = document.createElement('div');
document.body.appendChild(div);
// eslint-disable-next-line no-use-before-define
let currentConfig = { ...config, close, visible: true } as any;
function destroy(...args: any[]) {
const unmountResult = ReactDOM.unmountComponentAtNode(div);
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div);
}
const triggerCancel = args.some(param => param && param.triggerCancel);
if (config.onCancel && triggerCancel) {
config.onCancel(...args);
}
for (let i = 0; i < destroyFns.length; i++) {
const fn = destroyFns[i];
// eslint-disable-next-line no-use-before-define
if (fn === close) {
destroyFns.splice(i, 1);
break;
}
}
}
function render(props: any) {
ReactDOM.render(<ConfirmDialog {...props} getContainer={false} />, div);
}
function close(...args: any[]) {
currentConfig = {
...currentConfig,
@ -146,28 +170,6 @@ export default function confirm(config: ModalFuncProps) {
render(currentConfig);
}
function destroy(...args: any[]) {
const unmountResult = ReactDOM.unmountComponentAtNode(div);
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div);
}
const triggerCancel = args.some(param => param && param.triggerCancel);
if (config.onCancel && triggerCancel) {
config.onCancel(...args);
}
for (let i = 0; i < destroyFns.length; i++) {
const fn = destroyFns[i];
if (fn === close) {
destroyFns.splice(i, 1);
break;
}
}
}
function render(props: any) {
ReactDOM.render(<ConfirmDialog {...props} getContainer={false}/>, div);
}
render(currentConfig);
destroyFns.push(close);

View File

@ -6,6 +6,16 @@ import Icon from '../icon';
export { ActionButtonProps } from './ActionButton';
export { ModalProps, ModalFuncProps } from './Modal';
function modalWarn(props: ModalFuncProps) {
const config = {
type: 'warning',
icon: <Icon type="exclamation-circle" />,
okCancel: false,
...props,
};
return confirm(config);
}
Modal.info = function(props: ModalFuncProps) {
const config = {
type: 'info',
@ -36,15 +46,9 @@ Modal.error = function(props: ModalFuncProps) {
return confirm(config);
};
Modal.warning = Modal.warn = function(props: ModalFuncProps) {
const config = {
type: 'warning',
icon: <Icon type="exclamation-circle" />,
okCancel: false,
...props,
};
return confirm(config);
};
Modal.warning = modalWarn;
Modal.warn = modalWarn;
Modal.confirm = function(props: ModalFuncProps) {
const config = {

View File

@ -108,7 +108,7 @@ function getNotificationInstance(
className: `${prefixCls}-${placement}`,
style: getPlacementStyle(placement, top, bottom),
getContainer,
closeIcon: <Icon className={`${prefixCls}-close-icon`} type={'close'} />,
closeIcon: <Icon className={`${prefixCls}-close-icon`} type="close" />,
},
(notification: any) => {
notificationInstance[cacheKey] = notification;

View File

@ -44,7 +44,8 @@ class Popconfirm extends React.Component<PopconfirmProps, PopconfirmState> {
static getDerivedStateFromProps(nextProps: PopconfirmProps) {
if ('visible' in nextProps) {
return { visible: nextProps.visible };
} else if ('defaultVisible' in nextProps) {
}
if ('defaultVisible' in nextProps) {
return { visible: nextProps.defaultVisible };
}
return null;
@ -91,7 +92,7 @@ class Popconfirm extends React.Component<PopconfirmProps, PopconfirmState> {
};
setVisible(visible: boolean, e?: React.MouseEvent<HTMLButtonElement>) {
const props = this.props;
const { props } = this;
if (!('visible' in props)) {
this.setState({ visible });
}

View File

@ -18,6 +18,7 @@ interface LineProps extends ProgressProps {
*/
export const sortGradient = (gradients: ProgressGradient) => {
let tempArr = [];
// eslint-disable-next-line no-restricted-syntax
for (const [key, value] of Object.entries(gradients)) {
const formatKey = parseFloat(key.replace(/%/g, ''));
if (isNaN(formatKey)) {

View File

@ -1,6 +1,7 @@
import * as PropTypes from 'prop-types';
import * as React from 'react';
import classNames from 'classnames';
import omit from 'omit.js';
import Icon from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { tuple } from '../_util/type';
@ -98,26 +99,8 @@ export default class Progress extends React.Component<ProgressProps> {
}
renderProgress = ({ getPrefixCls }: ConfigConsumerProps) => {
const props = this.props;
const {
prefixCls: customizePrefixCls,
className,
percent = 0,
status,
format,
trailColor,
size,
successPercent,
type,
strokeWidth,
width,
showInfo,
gapDegree = 0,
gapPosition,
strokeColor,
strokeLinecap = 'round',
...restProps
} = props;
const { props } = this;
const { prefixCls: customizePrefixCls, className, size, type, showInfo, ...restProps } = props;
const prefixCls = getPrefixCls('progress', customizePrefixCls);
const progressStatus = this.getProgressStatus();
const progressInfo = this.renderProcessInfo(prefixCls, progressStatus);
@ -149,7 +132,22 @@ export default class Progress extends React.Component<ProgressProps> {
);
return (
<div {...restProps} className={classString}>
<div
{...omit(restProps, [
'status',
'format',
'trailColor',
'successPercent',
'strokeWidth',
'width',
'gapDegree',
'gapPosition',
'strokeColor',
'strokeLinecap',
'percent',
])}
className={classString}
>
{progress}
</div>
);

View File

@ -34,10 +34,10 @@
position: relative;
display: inline-block;
width: 100%;
overflow: hidden;
vertical-align: middle;
background-color: @progress-remaining-color;
border-radius: @progress-radius;
overflow: hidden;
}
&-circle-trail {

View File

@ -38,14 +38,14 @@ class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
return {
value: nextProps.value,
};
} else {
const checkedValue = getCheckedValue(nextProps.children);
if (checkedValue) {
return {
value: checkedValue.value,
};
}
}
const checkedValue = getCheckedValue(nextProps.children);
if (checkedValue) {
return {
value: checkedValue.value,
};
}
return null;
}
@ -89,14 +89,14 @@ class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
});
}
const onChange = this.props.onChange;
const { onChange } = this.props;
if (onChange && value !== lastValue) {
onChange(ev);
}
};
renderGroup = ({ getPrefixCls }: ConfigConsumerProps) => {
const props = this.props;
const { props } = this;
const { prefixCls: customizePrefixCls, className = '', options, buttonStyle } = props;
const prefixCls = getPrefixCls('radio', customizePrefixCls);
const groupPrefixCls = `${prefixCls}-group`;
@ -109,17 +109,16 @@ class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
className,
);
let children: React.ReactChildren[] | React.ReactElement<any>[] | React.ReactNode =
props.children;
let { children } = props;
// 如果存在 options, 优先使用
if (options && options.length > 0) {
children = options.map((option, index) => {
children = options.map(option => {
if (typeof option === 'string') {
// 此处类型自动推导为 string
return (
<Radio
key={index}
key={option}
prefixCls={prefixCls}
disabled={this.props.disabled}
value={option}
@ -128,20 +127,19 @@ class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
{option}
</Radio>
);
} else {
// 此处类型自动推导为 { label: string value: string }
return (
<Radio
key={index}
prefixCls={prefixCls}
disabled={option.disabled || this.props.disabled}
value={option.value}
checked={this.state.value === option.value}
>
{option.label}
</Radio>
);
}
// 此处类型自动推导为 { label: string value: string }
return (
<Radio
key={`radio-group-value-options-${option.value}`}
prefixCls={prefixCls}
disabled={option.disabled || this.props.disabled}
value={option.value}
checked={this.state.value === option.value}
>
{option.label}
</Radio>
);
});
}

View File

@ -10,6 +10,7 @@ import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export default class Radio extends React.Component<RadioProps, {}> {
static Group: typeof RadioGroup;
static Button: typeof RadioButton;
static defaultProps = {
@ -32,14 +33,6 @@ export default class Radio extends React.Component<RadioProps, {}> {
);
}
focus() {
this.rcCheckbox.focus();
}
blur() {
this.rcCheckbox.blur();
}
saveCheckbox = (node: any) => {
this.rcCheckbox = node;
};
@ -54,6 +47,14 @@ export default class Radio extends React.Component<RadioProps, {}> {
}
};
focus() {
this.rcCheckbox.focus();
}
blur() {
this.rcCheckbox.blur();
}
renderRadio = ({ getPrefixCls }: ConfigConsumerProps) => {
const { props, context } = this;
const { prefixCls: customizePrefixCls, className, children, style, ...restProps } = props;
@ -73,6 +74,7 @@ export default class Radio extends React.Component<RadioProps, {}> {
});
return (
// eslint-disable-next-line jsx-a11y/label-has-associated-control
<label
className={wrapperClassString}
style={style}

View File

@ -38,14 +38,6 @@ export default class Rate extends React.Component<RateProps, any> {
private rcRate: any;
focus() {
this.rcRate.focus();
}
blur() {
this.rcRate.blur();
}
saveRate = (node: any) => {
this.rcRate = node;
};
@ -57,6 +49,14 @@ export default class Rate extends React.Component<RateProps, any> {
return <Tooltip title={tooltips[index]}>{node}</Tooltip>;
};
focus() {
this.rcRate.focus();
}
blur() {
this.rcRate.blur();
}
renderRate = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls, ...restProps } = this.props;

View File

@ -1,6 +1,6 @@
import * as React from 'react';
export default () => (
const NoFound = () => (
<svg width="252" height="294">
<defs>
<path d="M0 .387h251.772v251.772H0z" />
@ -282,3 +282,5 @@ export default () => (
</g>
</svg>
);
export default NoFound;

View File

@ -1,6 +1,6 @@
import * as React from 'react';
export default () => (
const ServerError = () => (
<svg width="254" height="294">
<defs>
<path d="M0 .335h253.49v253.49H0z" />
@ -327,3 +327,5 @@ export default () => (
</g>
</svg>
);
export default ServerError;

View File

@ -1,6 +1,6 @@
import * as React from 'react';
export default () => (
const Unauthorized = () => (
<svg width="251" height="294">
<g fill="none" fillRule="evenodd">
<path
@ -276,3 +276,5 @@ export default () => (
</g>
</svg>
);
export default Unauthorized;

View File

@ -2,8 +2,8 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import RcSelect, { Option, OptGroup } from 'rc-select';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
import omit from 'omit.js';
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
import warning from '../_util/warning';
import Icon from '../icon';
import { tuple } from '../_util/type';
@ -57,10 +57,7 @@ export interface SelectProps<T = SelectValue> extends AbstractSelectProps {
mode?: 'default' | 'multiple' | 'tags' | 'combobox' | string;
optionLabelProp?: string;
firstActiveValue?: string | string[];
onChange?: (
value: T,
option: React.ReactElement<any> | React.ReactElement<any>[],
) => void;
onChange?: (value: T, option: React.ReactElement<any> | React.ReactElement<any>[]) => void;
onSelect?: (value: T extends (infer I)[] ? I : T, option: React.ReactElement<any>) => void;
onDeselect?: (value: T extends (infer I)[] ? I : T) => void;
onBlur?: (value: T) => void;
@ -117,6 +114,7 @@ const SelectPropTypes = {
export default class Select<T = SelectValue> extends React.Component<SelectProps<T>, {}> {
static Option = Option as React.ClassicComponentClass<OptionProps>;
static OptGroup = OptGroup as React.ClassicComponentClass<OptGroupProps>;
static SECRET_COMBOBOX_MODE_DO_NOT_USE = 'SECRET_COMBOBOX_MODE_DO_NOT_USE';
@ -143,18 +141,6 @@ export default class Select<T = SelectValue> extends React.Component<SelectProps
);
}
focus() {
this.rcSelect.focus();
}
blur() {
this.rcSelect.blur();
}
saveSelect = (node: any) => {
this.rcSelect = node;
};
getNotFoundContent(renderEmpty: RenderEmptyHandler) {
const { notFoundContent } = this.props;
if (notFoundContent !== undefined) {
@ -176,6 +162,18 @@ export default class Select<T = SelectValue> extends React.Component<SelectProps
// // return notFoundContent === undefined ? locale.notFoundContent : notFoundContent;
}
saveSelect = (node: any) => {
this.rcSelect = node;
};
focus() {
this.rcSelect.focus();
}
blur() {
this.rcSelect.blur();
}
isCombobox() {
const { mode } = this.props;
return mode === 'combobox' || mode === Select.SECRET_COMBOBOX_MODE_DO_NOT_USE;

View File

@ -9,6 +9,7 @@ export interface SkeletonAvatarProps {
shape?: 'circle' | 'square';
}
// eslint-disable-next-line react/prefer-stateless-function
class SkeletonAvatar extends React.Component<SkeletonAvatarProps, any> {
static defaultProps: Partial<SkeletonAvatarProps> = {
size: 'large',

View File

@ -27,6 +27,7 @@ class Paragraph extends React.Component<SkeletonParagraphProps, {}> {
render() {
const { prefixCls, className, style, rows } = this.props;
const rowList = [...Array(rows)].map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<li key={index} style={{ width: this.getWidth(index) }} />
));
return (

View File

@ -1,3 +1,4 @@
/* eslint-disable jsx-a11y/heading-has-content */
import * as React from 'react';
import classNames from 'classnames';
@ -8,12 +9,8 @@ export interface SkeletonTitleProps {
width?: number | string;
}
class Title extends React.Component<SkeletonTitleProps, any> {
render() {
const { prefixCls, className, width, style } = this.props;
return <h3 className={classNames(prefixCls, className)} style={{ width, ...style }} />;
}
}
const Title = ({ prefixCls, className, width, style }: SkeletonTitleProps) => (
<h3 className={classNames(prefixCls, className)} style={{ width, ...style }} />
);
export default Title;

Some files were not shown because too many files have changed in this diff Show More