Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Refactor and apply wave click animation to more components
now support Button, Tag, Radio.Button and switch
  • Loading branch information
afc163 committed Aug 11, 2018
1 parent d8fd521 commit 9cf6ae6
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 75 deletions.
101 changes: 101 additions & 0 deletions components/_util/wave.tsx
@@ -0,0 +1,101 @@
import * as React from 'react';
import { findDOMNode } from 'react-dom';
import TransitionEvents from 'css-animation/lib/Event';

export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
private instance?: {
cancel: () => void;
};

private styleForPesudo: HTMLStyleElement | null;

isNotGrey(color: string) {
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
console.log(match);
if (match && match[1] && match[2] && match[3]) {
return !(match[1] === match[2] && match[2] === match[3]);
}
return true;
}

onClick = (node: HTMLElement) => {
this.removeExtraStyleNode();
const { insertExtraNode } = this.props;
const extraNode = document.createElement('div');
extraNode.className = 'ant-click-animating-node';
const attributeName = insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
node.removeAttribute(attributeName);
node.setAttribute(attributeName, 'true');
// Get wave color from target
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color');
// Not white or transparnt or grey
if (waveColor &&
waveColor !== '#ffffff' &&
waveColor !== 'rgb(255, 255, 255)' &&
this.isNotGrey(waveColor) &&
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent') {
extraNode.style.borderColor = waveColor;
this.styleForPesudo = document.createElement('style');
this.styleForPesudo.innerHTML =
`[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
document.body.appendChild(this.styleForPesudo);
}
if (insertExtraNode) {
node.appendChild(extraNode);
}
const transitionEnd = () => {
node.removeAttribute(attributeName);
this.removeExtraStyleNode();
if (insertExtraNode) {
node.removeChild(extraNode);
}
TransitionEvents.removeEndEventListener(node, transitionEnd);
};
TransitionEvents.addEndEventListener(node, transitionEnd);
}

bindAnimationEvent = (node: HTMLElement) => {
if (node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0) {
return;
}
const onClick = (e: MouseEvent) => {
// Fix radio button click twice
if ((e.target as HTMLElement).tagName === 'INPUT') {
return;
}
setTimeout(() => this.onClick(node), 0);
};
node.addEventListener('click', onClick, true);
return {
cancel: () => {
node.removeEventListener('click', onClick, true);
},
};
}

removeExtraStyleNode() {
if (this.styleForPesudo && document.body.contains(this.styleForPesudo)) {
document.body.removeChild(this.styleForPesudo);
this.styleForPesudo = null;
}
}

componentDidMount() {
this.instance = this.bindAnimationEvent(findDOMNode(this) as HTMLElement);
}

componentWillUnmount() {
if (this.instance) {
this.instance.cancel();
}
}

render() {
return this.props.children;
}
}
29 changes: 12 additions & 17 deletions components/button/button.tsx
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { findDOMNode } from 'react-dom';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import clickAnimation from './clickAnimation';
import Wave from '../_util/wave';
import Icon from '../icon';
import Group from './button-group';

Expand Down Expand Up @@ -87,10 +87,7 @@ export default class Button extends React.Component<ButtonProps, any> {
block: PropTypes.bool,
};

delayTimeout: number;
clickAnimation: {
cancel: () => void;
};
private delayTimeout: number;

constructor(props: ButtonProps) {
super(props);
Expand All @@ -102,7 +99,6 @@ export default class Button extends React.Component<ButtonProps, any> {

componentDidMount() {
this.fixTwoCNChar();
this.clickAnimation = clickAnimation(findDOMNode(this) as HTMLElement);
}

componentWillReceiveProps(nextProps: ButtonProps) {
Expand All @@ -128,9 +124,6 @@ export default class Button extends React.Component<ButtonProps, any> {
if (this.delayTimeout) {
clearTimeout(this.delayTimeout);
}
if (this.clickAnimation) {
this.clickAnimation.cancel();
}
}

fixTwoCNChar() {
Expand Down Expand Up @@ -213,14 +206,16 @@ export default class Button extends React.Component<ButtonProps, any> {
const { htmlType, ...otherProps } = rest;

return (
<button
{...otherProps}
type={htmlType || 'button'}
className={classes}
onClick={this.handleClick}
>
{iconNode}{kids}
</button>
<Wave>
<button
{...otherProps}
type={htmlType || 'button'}
className={classes}
onClick={this.handleClick}
>
{iconNode}{kids}
</button>
</Wave>
);
}
}
Expand Down
20 changes: 0 additions & 20 deletions components/button/clickAnimation.tsx

This file was deleted.

30 changes: 0 additions & 30 deletions components/button/style/index.less
Expand Up @@ -8,7 +8,6 @@
@btn-ghost-color: @text-color;
@btn-ghost-bg: transparent;
@btn-ghost-border: @border-color-base;
@btn-animation-width: 6px;

// Button styles
// -----------------------------
Expand Down Expand Up @@ -132,10 +131,6 @@
margin-left: 8px;
}

&-danger[ant-click-animating]:after {
border-color: @btn-danger-color;
}

&-background-ghost {
background: transparent !important;
border-color: #fff;
Expand Down Expand Up @@ -164,17 +159,6 @@
}
}

@keyframes buttonEffect {
to {
opacity: 0;
top: -@btn-animation-width;
left: -@btn-animation-width;
bottom: -@btn-animation-width;
right: -@btn-animation-width;
border-width: @btn-animation-width;
}
}

a.@{btn-prefix-cls} {
line-height: @btn-height-base - 2px;
&-lg {
Expand All @@ -184,17 +168,3 @@ a.@{btn-prefix-cls} {
line-height: @btn-height-sm - 2px;
}
}

[ant-click-animating]:after {
content: '';
position: absolute;
top: -1px;
left: -1px;
bottom: -1px;
right: -1px;
border-radius: inherit;
border: 0 solid @primary-color;
opacity: 0.4;
animation: buttonEffect .4s cubic-bezier(.25, .8, .25, 1);
display: block;
}
5 changes: 4 additions & 1 deletion components/radio/radioButton.tsx
Expand Up @@ -3,6 +3,7 @@ import * as PropTypes from 'prop-types';
import { AbstractCheckboxProps } from '../checkbox/Checkbox';
import Radio from './radio';
import { RadioChangeEvent } from './interface';
import Wave from '../_util/wave';

export type RadioButtonProps = AbstractCheckboxProps<RadioChangeEvent>;

Expand All @@ -24,7 +25,9 @@ export default class RadioButton extends React.Component<RadioButtonProps, any>
}

return (
<Radio {...radioProps} />
<Wave>
<Radio {...radioProps} />
</Wave>
);
}
}
31 changes: 31 additions & 0 deletions components/style/core/motion/other.less
Expand Up @@ -3,3 +3,34 @@
transform: rotate(360deg);
}
}

[ant-click-animating],
[ant-click-animating-without-extra-node] {
position: relative;
}

[ant-click-animating-without-extra-node]:after,
.ant-click-animating-node {
content: '';
position: absolute;
top: -1px;
left: -1px;
bottom: -1px;
right: -1px;
border-radius: inherit;
border: 0 solid @primary-color;
opacity: 0.4;
animation: waveEffect .4s cubic-bezier(.25, .8, .25, 1);
display: block;
}

@keyframes waveEffect {
to {
opacity: 0;
top: -@wave-animation-width;
left: -@wave-animation-width;
bottom: -@wave-animation-width;
right: -@wave-animation-width;
border-width: @wave-animation-width;
}
}
4 changes: 4 additions & 0 deletions components/style/themes/default.less
Expand Up @@ -486,4 +486,8 @@
// ---
@message-notice-content-padding: 10px 16px;

// Motion
// ---
@wave-animation-width: 6px;

@import "./default.deperated.less";
13 changes: 8 additions & 5 deletions components/switch/index.tsx
Expand Up @@ -3,6 +3,7 @@ import * as PropTypes from 'prop-types';
import RcSwitch from 'rc-switch';
import classNames from 'classnames';
import omit from 'omit.js';
import Wave from '../_util/wave';

export interface SwitchProps {
prefixCls?: string;
Expand Down Expand Up @@ -51,11 +52,13 @@ export default class Switch extends React.Component<SwitchProps, {}> {
[`${prefixCls}-loading`]: loading,
});
return (
<RcSwitch
{...omit(this.props, ['loading'])}
className={classes}
ref={this.saveSwitch}
/>
<Wave insertExtraNode>
<RcSwitch
{...omit(this.props, ['loading'])}
className={classes}
ref={this.saveSwitch}
/>
</Wave>
);
}
}
8 changes: 7 additions & 1 deletion components/tag/CheckableTag.tsx
Expand Up @@ -23,6 +23,12 @@ export default class CheckableTag extends React.Component<CheckableTagProps> {
}, className);

delete (restProps as any).onChange; // TypeScript cannot check delete now.
return <div {...restProps as any} className={cls} onClick={this.handleClick} />;
return (
<div
{...restProps as any}
className={cls}
onClick={this.handleClick}
/>
);
}
}
5 changes: 4 additions & 1 deletion components/tag/index.tsx
Expand Up @@ -6,6 +6,7 @@ import omit from 'omit.js';
import { polyfill } from 'react-lifecycles-compat';
import Icon from '../icon';
import CheckableTag from './CheckableTag';
import Wave from '../_util/wave';

export { CheckableTagProps } from './CheckableTag';

Expand Down Expand Up @@ -148,7 +149,9 @@ class Tag extends React.Component<TagProps, TagState> {
transitionAppear
onEnd={this.animationEnd}
>
{tag}
<Wave>
{tag}
</Wave>
</Animate>
);
}
Expand Down

0 comments on commit 9cf6ae6

Please sign in to comment.