import { colors } from '@0x/react-shared'; import { errorUtils } from '@0x/utils'; import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; const COMPLETE_STATE_SHOW_LENGTH_MS = 2000; enum ButtonState { Ready, Loading, Complete, } interface LifeCycleRaisedButtonProps { isHidden?: boolean; isDisabled?: boolean; isPrimary?: boolean; labelReady: React.ReactNode | string; labelLoading: React.ReactNode | string; labelComplete: React.ReactNode | string; onClickAsyncFn: () => Promise; backgroundColor?: string; labelColor?: string; } interface LifeCycleRaisedButtonState { buttonState: ButtonState; } export class LifeCycleRaisedButton extends React.Component { public static defaultProps: Partial = { isDisabled: false, backgroundColor: colors.white, labelColor: colors.darkGrey, }; private _buttonTimeoutId: number; private _didUnmount: boolean; constructor(props: LifeCycleRaisedButtonProps) { super(props); this.state = { buttonState: ButtonState.Ready, }; } public componentWillUnmount(): void { clearTimeout(this._buttonTimeoutId); this._didUnmount = true; } public render(): React.ReactNode { if (this.props.isHidden) { return ; } let label; switch (this.state.buttonState) { case ButtonState.Ready: label = this.props.labelReady; break; case ButtonState.Loading: label = this.props.labelLoading; break; case ButtonState.Complete: label = this.props.labelComplete; break; default: throw errorUtils.spawnSwitchErr('ButtonState', this.state.buttonState); } return ( ); } public async onClickAsync(): Promise { this.setState({ buttonState: ButtonState.Loading, }); const didSucceed = await this.props.onClickAsyncFn(); if (this._didUnmount) { return; // noop since unmount called before async callback returned. } if (didSucceed) { this.setState({ buttonState: ButtonState.Complete, }); this._buttonTimeoutId = window.setTimeout(() => { this.setState({ buttonState: ButtonState.Ready, }); }, COMPLETE_STATE_SHOW_LENGTH_MS); } else { this.setState({ buttonState: ButtonState.Ready, }); } } }