diff --git a/.eslintrc.js b/.eslintrc.js index 800a1e0285..57aad8877f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,11 @@ const eslintrc = { - extends: ['airbnb', 'prettier', 'plugin:jest/recommended', 'plugin:react/recommended'], + extends: [ + 'airbnb', + 'prettier', + 'plugin:jest/recommended', + 'plugin:react/recommended', + 'plugin:import/typescript', + ], env: { browser: true, node: true, @@ -14,6 +20,15 @@ const eslintrc = { }, 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, @@ -34,12 +49,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, }, }; @@ -61,11 +98,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, }); } diff --git a/components/_util/motion.tsx b/components/_util/motion.tsx index 9ac7a935bd..62d4cf3a4a 100644 --- a/components/_util/motion.tsx +++ b/components/_util/motion.tsx @@ -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; diff --git a/components/_util/throttleByAnimationFrame.tsx b/components/_util/throttleByAnimationFrame.tsx index 401c384936..4793fa5c8f 100644 --- a/components/_util/throttleByAnimationFrame.tsx +++ b/components/_util/throttleByAnimationFrame.tsx @@ -26,6 +26,7 @@ export function throttleByAnimationFrameDecorator() { return { configurable: true, get() { + /* eslint-disable no-prototype-builtins */ if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) { return fn; } diff --git a/components/_util/transButton.tsx b/components/_util/transButton.tsx index c9a49bc764..33199b7a20 100644 --- a/components/_util/transButton.tsx +++ b/components/_util/transButton.tsx @@ -20,6 +20,7 @@ const inlineStyle: React.CSSProperties = { class TransButton extends React.Component { div?: HTMLDivElement; + lastKeyCode?: number; onKeyDown: React.KeyboardEventHandler = event => { diff --git a/components/_util/wave.tsx b/components/_util/wave.tsx index fffe9a33c6..d7ce3826a8 100644 --- a/components/_util/wave.tsx +++ b/components/_util/wave.tsx @@ -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 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; diff --git a/components/affix/index.tsx b/components/affix/index.tsx index 15179f3962..e1b025a708 100644 --- a/components/affix/index.tsx +++ b/components/affix/index.tsx @@ -63,7 +63,9 @@ class Affix extends React.Component { }; placeholderNode: HTMLDivElement; + fixedNode: HTMLDivElement; + private timeout: number; // Event handler @@ -75,7 +77,7 @@ class Affix extends React.Component { this.timeout = setTimeout(() => { addObserveTarget(target(), this); // Mock Event object. - this.updatePosition({} as Event); + this.updatePosition(); }); } } @@ -93,7 +95,7 @@ class Affix extends React.Component { 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 { 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 { } return offsetTop; }; + getOffsetBottom = () => { return this.props.offsetBottom; }; @@ -145,60 +148,6 @@ class Affix extends React.Component { }; // =================== 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 { 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; diff --git a/components/affix/utils.ts b/components/affix/utils.ts index 9f8747f07a..aee6c16122 100644 --- a/components/affix/utils.ts +++ b/components/affix/utils.ts @@ -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(); }); }); }); diff --git a/components/alert/__tests__/__snapshots__/demo.test.js.snap b/components/alert/__tests__/__snapshots__/demo.test.js.snap index e6f0a71274..568f0af170 100644 --- a/components/alert/__tests__/__snapshots__/demo.test.js.snap +++ b/components/alert/__tests__/__snapshots__/demo.test.js.snap @@ -69,6 +69,7 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = ` { } // use outline icon in alert with description - if (!!description) { + if (description) { iconTheme = 'outlined'; } } @@ -143,7 +143,12 @@ export default class Alert extends React.Component { ); const closeIcon = closable ? ( - + {closeText ? ( {closeText} ) : ( diff --git a/components/anchor/Anchor.tsx b/components/anchor/Anchor.tsx index 289ec857dd..98ff8085c9 100644 --- a/components/anchor/Anchor.tsx +++ b/components/anchor/Anchor.tsx @@ -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 { }; 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 { 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 { 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 { 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 { } }; - saveInkNode = (node: HTMLSpanElement) => { - this.inkNode = node; - }; - renderAnchor = ({ getPrefixCls }: ConfigConsumerProps) => { const { prefixCls: customizePrefixCls, diff --git a/components/auto-complete/InputElement.tsx b/components/auto-complete/InputElement.tsx index fbedf5bc34..65a33022f6 100644 --- a/components/auto-complete/InputElement.tsx +++ b/components/auto-complete/InputElement.tsx @@ -9,13 +9,19 @@ export default class InputElement extends React.Component { - 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) => { diff --git a/components/auto-complete/index.tsx b/components/auto-complete/index.tsx index e75ffc7d00..6fcf2f2977 100755 --- a/components/auto-complete/index.tsx +++ b/components/auto-complete/index.tsx @@ -51,6 +51,7 @@ function isSelectOptionOrSelectOptGroup(child: any): Boolean { export default class AutoComplete extends React.Component { static Option = Option as React.ClassicComponentClass; + static OptGroup = OptGroup as React.ClassicComponentClass; static defaultProps = { @@ -63,6 +64,10 @@ export default class AutoComplete extends React.Component 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 this.select.blur(); } - saveSelect = (node: any) => { - this.select = node; - }; - renderAutoComplete = ({ getPrefixCls }: ConfigConsumerProps) => { const { prefixCls: customizePrefixCls, diff --git a/components/avatar/index.tsx b/components/avatar/index.tsx index bef60e80ac..f3d0536cbe 100644 --- a/components/avatar/index.tsx +++ b/components/avatar/index.tsx @@ -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 { }; private avatarNode: HTMLElement; + private avatarChildren: HTMLElement; + private lastChildrenWidth: number; + private lastNodeWidth: number; componentDidMount() { @@ -127,7 +130,7 @@ export default class Avatar extends React.Component { } : {}; - let children = this.props.children; + let { children } = this.props; if (src && isImgExist) { children = {alt}; } else if (icon) { diff --git a/components/back-top/index.tsx b/components/back-top/index.tsx index 8a9b516273..f994cc573c 100755 --- a/components/back-top/index.tsx +++ b/components/back-top/index.tsx @@ -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 { }; } + 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 { (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 { }); }; - 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); diff --git a/components/badge/ScrollNumber.tsx b/components/badge/ScrollNumber.tsx index acc028cd35..1ca9865fd1 100644 --- a/components/badge/ScrollNumber.tsx +++ b/components/badge/ScrollNumber.tsx @@ -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[] = []; + for (let i = 0; i < 30; i++) { + const currentClassName = position === i ? 'current' : ''; + childrenToReturn.push( +

+ {i % 10} +

, + ); + } + + return childrenToReturn; +} + export interface ScrollNumberProps { prefixCls?: string; className?: string; @@ -62,6 +75,21 @@ class ScrollNumber extends Component { }; } + componentDidUpdate(_: any, prevState: ScrollNumberState) { + this.lastCount = prevState.count; + const { animateStarted } = this.state; + if (animateStarted) { + /* eslint-disable 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 { 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 { } }; - renderNumberList(position: number) { - const childrenToReturn: React.ReactElement[] = []; - for (let i = 0; i < 30; i++) { - const currentClassName = position === i ? 'current' : ''; - childrenToReturn.push( -

- {i % 10} -

, - ); - } - - 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 { }, key: i, }, - this.renderNumberList(position), + renderNumberList(position), ); } diff --git a/components/badge/index.tsx b/components/badge/index.tsx index 2c5060c924..7fa8051fc2 100644 --- a/components/badge/index.tsx +++ b/components/badge/index.tsx @@ -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 { 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 { : 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 { 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 { const styleWithOffset = this.getStyleWithOffset(); const statusTextColor = styleWithOffset && styleWithOffset.color; return ( - + {text} @@ -213,7 +220,7 @@ export default class Badge extends React.Component { } return ( - + {children} { 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 +122,7 @@ export default class Breadcrumb extends React.Component { ); }); }; + renderBreadcrumb = ({ getPrefixCls }: ConfigConsumerProps) => { let crumbs; const { diff --git a/components/breadcrumb/BreadcrumbItem.tsx b/components/breadcrumb/BreadcrumbItem.tsx index 6bf9470280..cbe06ca3e3 100644 --- a/components/breadcrumb/BreadcrumbItem.tsx +++ b/components/breadcrumb/BreadcrumbItem.tsx @@ -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 { - 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 = ( - + {children} ); } else { link = ( - + {children} ); @@ -82,6 +77,7 @@ export default class BreadcrumbItem extends React.Component{this.renderBreadcrumbItem}; } diff --git a/components/button/__tests__/__snapshots__/demo.test.js.snap b/components/button/__tests__/__snapshots__/demo.test.js.snap index 0bbbd84d0c..7c62ab8d3f 100644 --- a/components/button/__tests__/__snapshots__/demo.test.js.snap +++ b/components/button/__tests__/__snapshots__/demo.test.js.snap @@ -846,6 +846,7 @@ exports[`renders ./components/button/demo/size.md correctly 1`] = ` >