react-awesome-button-namdaoduy
Version:
Performant, extendable, highly customisable, production ready React Component that renders an animated basic set of UI buttons
190 lines (187 loc) • 4.93 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { AwesomeButton } from '../../index';
import {
getClassName,
setCssEndEvent,
} from '../../helpers/components';
const ROOTELM = 'aws-btn';
const LOADING_ANIMATION_STEPS = 4;
export default class AwesomeProgress extends React.Component {
static propTypes = {
action: PropTypes.func,
loadingLabel: PropTypes.string,
resultLabel: PropTypes.string,
rootElement: PropTypes.node,
cssModule: PropTypes.object,
children: PropTypes.node,
size: PropTypes.string,
type: PropTypes.string,
disabled: PropTypes.bool,
};
static defaultProps = {
action: null,
rootElement: null,
loadingLabel: 'Wait..',
resultLabel: 'Success!',
cssModule: null,
children: null,
size: null,
type: null,
disabled: false,
};
constructor(props) {
super(props);
this.rootElement = props.rootElement || ROOTELM;
this.animationStage = 0;
this.loading = false;
this.state = {
loadingEnd: false,
loadingStart: false,
blocked: false,
active: false,
disabled: props.disabled,
};
}
componentDidMount() {
setCssEndEvent(this.content, 'transition', this.clearStagedWrapperAnimation.bind(this));
}
getRootClassName() {
const {
rootElement,
} = this;
const {
loadingStart,
loadingEnd,
} = this.state;
const className = [
(loadingStart && `${rootElement}--start`) || null,
(loadingEnd && `${rootElement}--end`) || null,
`${rootElement}--progress`,
];
return className.join(' ').trim().replace(/[\s]+/ig, ' ');
}
endLoading() {
this.setState({
loadingEnd: true,
});
this.animationStage = 1;
}
startLoading() {
this.loading = true;
this.setState({
blocked: true,
active: true,
}, () => {
/*
To avoid the button eventual flickering I've changed the display strategy
for that to work in a controlled way we need to set the loadingStart
at least one painting cycle ahead
*/
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
this.setState({
loadingStart: true,
});
});
});
});
}
clearLoading(callback) {
this.loading = false;
this.setState({
loadingStart: false,
loadingEnd: false,
active: false,
}, callback);
}
clearStagedWrapperAnimation() {
if (this.animationStage !== 0) {
if (this.animationStage === LOADING_ANIMATION_STEPS) {
this.animationStage = 0;
// hold life for 500ms before releasing the button;
setTimeout(() => {
if (typeof window !== 'undefined') {
window.requestAnimationFrame(() => {
this.clearLoading(() => {
setTimeout(() => {
this.setState({
blocked: false,
});
}, 500);
});
});
}
}, 500);
return;
}
this.animationStage += 1;
}
}
action = () => {
this.startLoading();
if (this.props.action) {
this.props.action(
this.container,
this.endLoading.bind(this),
);
}
}
moveEvents() {
const events = {
onMouseDown: (event) => {
if (this.state.disabled === true ||
this.loading === true ||
this.state.blocked === true ||
(event && event.nativeEvent.which !== 1)
) {
return;
}
this.loading = true;
},
onMouseUp: (event) => {
if (this.state.disabled === true ||
this.loading === true ||
this.state.blocked === true) {
event.preventDefault();
event.stopPropagation();
return;
}
this.action();
},
};
return events;
}
render() {
const {
children,
size,
cssModule,
loadingLabel,
resultLabel,
type,
} = this.props;
return (
<AwesomeButton
disabled={this.state.disabled}
size={size}
type={type}
className={this.getRootClassName()}
action={this.action}
cssModule={cssModule}
active={this.state.active}
blocked={this.state.blocked}
{... this.moveEvents()}
>
<span
ref={(content) => { this.content = content; }}
data-loading={(loadingLabel) || null}
data-status={(resultLabel) || null}
className={getClassName(`${this.rootElement}__progress`, cssModule)}
>
<span>{children}</span>
</span>
</AwesomeButton>
);
}
}