diff --git a/ActionButton.js b/ActionButton.js index ec1b9e3..65566ea 100644 --- a/ActionButton.js +++ b/ActionButton.js @@ -1,11 +1,11 @@ -import React, { Component, useState, useRef, useEffect } from "react"; +import React, { Component } from "react"; import PropTypes from "prop-types"; import { StyleSheet, Text, View, Animated, - TouchableOpacity + TouchableOpacity, } from "react-native"; import ActionButtonItem from "./ActionButtonItem"; import { @@ -17,160 +17,222 @@ import { DEFAULT_ACTIVE_OPACITY } from "./shared"; -const ActionButton = props => { - const [, setResetToken] = useState(props.resetToken); - const [active, setActive] = useState(props.active); - const anim = useRef(new Animated.Value(props.active ? 1 : 0)); - const timeout = useRef(null); - const mounted = useRef(false); +export default class ActionButton extends Component { + constructor(props) { + super(props); - useEffect(() => { - mounted.current = true; - - return () => { - mounted.current = false; - timeout.current && clearTimeout(timeout.current); + this.state = { + resetToken: props.resetToken, + active: props.active }; - }, []); - useEffect(() => { - if (props.active) { - Animated.spring(anim.current, { toValue: 1 }).start(); - setActive(true); - setResetToken(props.resetToken); - } else { - props.onReset && props.onReset(); + this.anim = new Animated.Value(props.active ? 1 : 0); + this.timeout = null; + } + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + clearTimeout(this.timeout); + } - Animated.spring(anim.current, { toValue: 0 }).start(); - timeout.current = setTimeout(() => { - setActive(false); - setResetToken(props.resetToken); - }, 250); + componentWillReceiveProps(nextProps) { + if (nextProps.resetToken !== this.state.resetToken) { + if (nextProps.active === false && this.state.active === true) { + if (this.props.onReset) this.props.onReset(); + Animated.spring(this.anim, { toValue: 0, useNativeDriver: false }).start(); + setTimeout( + () => + this.setState({ active: false, resetToken: nextProps.resetToken }), + 250 + ); + return; + } + + if (nextProps.active === true && this.state.active === false) { + Animated.spring(this.anim, { toValue: 1, useNativeDriver: false }).start(); + this.setState({ active: true, resetToken: nextProps.resetToken }); + return; + } + + this.setState({ + resetToken: nextProps.resetToken, + active: nextProps.active + }); } - }, [props.resetToken, props.active]); + } ////////////////////// // STYLESHEET GETTERS ////////////////////// - const getOrientation = () => { - return { alignItems: alignItemsMap[props.position] }; - }; + getOrientation() { + return { alignItems: alignItemsMap[this.props.position] }; + } - const getOffsetXY = () => { + getOffsetXY() { return { - // paddingHorizontal: props.offsetX, - paddingVertical: props.offsetY + // paddingHorizontal: this.props.offsetX, + paddingVertical: this.props.offsetY }; - }; + } - const getOverlayStyles = () => { + getOverlayStyles() { return [ styles.overlay, { - elevation: props.elevation, - zIndex: props.zIndex, - justifyContent: - props.verticalOrientation === "up" ? "flex-end" : "flex-start" + elevation: this.props.elevation, + zIndex: this.props.zIndex, + justifyContent: this.props.verticalOrientation === "up" + ? "flex-end" + : "flex-start" } ]; - }; + } + + ////////////////////// + // RENDER METHODS + ////////////////////// + + render() { + return ( + + + {this.props.backdrop} + + + {this.state.active && + !this.props.backgroundTappable && + this._renderTappableBackground()} + + {this.props.verticalOrientation === "up" && + this.props.children && + this._renderActions()} + {this._renderMainButton()} + {this.props.verticalOrientation === "down" && + this.props.children && + this._renderActions()} + + + ); + } - const _renderMainButton = () => { + _renderMainButton() { const animatedViewStyle = { transform: [ { - scale: anim.current.interpolate({ + scale: this.anim.interpolate({ inputRange: [0, 1], - outputRange: [1, props.outRangeScale] + outputRange: [1, this.props.outRangeScale] }) }, { - rotate: anim.current.interpolate({ + rotate: this.anim.interpolate({ inputRange: [0, 1], - outputRange: ["0deg", props.degrees + "deg"] + outputRange: ["0deg", this.props.degrees + "deg"] }) } ] }; const wrapperStyle = { - backgroundColor: anim.current.interpolate({ + backgroundColor: this.anim.interpolate({ inputRange: [0, 1], - outputRange: [props.buttonColor, props.btnOutRange || props.buttonColor] + outputRange: [ + this.props.buttonColor, + this.props.btnOutRange || this.props.buttonColor + ] }), - width: props.size, - height: props.size, - borderRadius: props.size / 2 + width: this.props.size, + height: this.props.size, + borderRadius: this.props.size / 2 }; const buttonStyle = { - width: props.size, - height: props.size, - borderRadius: props.size / 2, + width: this.props.size, + height: this.props.size, + borderRadius: this.props.size / 2, alignItems: "center", justifyContent: "center" }; - const Touchable = getTouchableComponent(props.useNativeFeedback); - const parentStyle = - isAndroid && props.fixNativeFeedbackRadius - ? { - right: props.offsetX, - zIndex: props.zIndex, - borderRadius: props.size / 2, - width: props.size - } - : { marginHorizontal: props.offsetX, zIndex: props.zIndex }; + const Touchable = getTouchableComponent(this.props.useNativeFeedback); + const parentStyle = isAndroid && + this.props.fixNativeFeedbackRadius + ? { + right: this.props.offsetX, + zIndex: this.props.zIndex, + borderRadius: this.props.size / 2, + width: this.props.size + } + : { marginHorizontal: this.props.offsetX, zIndex: this.props.zIndex }; return ( - { - props.onPress(); - if (props.children) animateButton(); + this.props.onPress(); + if (this.props.children) this.animateButton(); }} - onPressIn={props.onPressIn} - onPressOut={props.onPressOut} + onPressIn={this.props.onPressIn} + onPressOut={this.props.onPressOut} > - + - {_renderButtonIcon()} + {this._renderButtonIcon()} ); - }; - - const _renderButtonIcon = () => { - const { - icon, - renderIcon, - btnOutRangeTxt, - buttonTextStyle, - buttonText - } = props; - if (renderIcon) return renderIcon(active); + } + + _renderButtonIcon() { + const { icon, renderIcon, btnOutRangeTxt, buttonTextStyle, buttonText } = this.props; + if (renderIcon) return renderIcon(this.state.active); if (icon) { - console.warn( - "react-native-action-button: The `icon` prop is deprecated! Use `renderIcon` instead." - ); + console.warn('react-native-action-button: The `icon` prop is deprecated! Use `renderIcon` instead.'); return icon; } @@ -182,7 +244,7 @@ const ActionButton = props => { styles.btnText, buttonTextStyle, { - color: anim.current.interpolate({ + color: this.anim.interpolate({ inputRange: [0, 1], outputRange: [textColor, btnOutRangeTxt || textColor] }) @@ -192,26 +254,26 @@ const ActionButton = props => { {buttonText} ); - }; + } - const _renderActions = () => { - const { children, verticalOrientation } = props; + _renderActions() { + const { children, verticalOrientation } = this.props; - if (!active) return null; + if (!this.state.active) return null; let actionButtons = !Array.isArray(children) ? [children] : children; - actionButtons = actionButtons.filter( - actionButton => typeof actionButton == "object" - ); + actionButtons = actionButtons.filter( actionButton => (typeof actionButton == 'object') ) const actionStyle = { flex: 1, alignSelf: "stretch", // backgroundColor: 'purple', justifyContent: verticalOrientation === "up" ? "flex-end" : "flex-start", - paddingTop: props.verticalOrientation === "down" ? props.spacing : 0, - zIndex: props.zIndex + paddingTop: this.props.verticalOrientation === "down" + ? this.props.spacing + : 0, + zIndex: this.props.zIndex }; return ( @@ -219,14 +281,14 @@ const ActionButton = props => { {actionButtons.map((ActionButton, idx) => ( { - if (props.autoInactive) { - timeout.current = setTimeout(reset, 200); + if (this.props.autoInactive) { + this.timeout = setTimeout(this.reset.bind(this), 200); } ActionButton.props.onPress(); }} @@ -234,84 +296,50 @@ const ActionButton = props => { ))} ); - }; + } - const _renderTappableBackground = () => { + _renderTappableBackground() { return ( ); - }; + } ////////////////////// // Animation Methods ////////////////////// - const animateButton = (animate = true) => { - if (active) return reset(animate); + animateButton(animate = true) { + if (this.state.active) return this.reset(); if (animate) { - Animated.spring(anim.current, { toValue: 1 }).start(); + Animated.spring(this.anim, { toValue: 1, useNativeDriver: false }).start(); } else { - anim.current.setValue(1); + this.anim.setValue(1); } - setActive(true); - }; + this.setState({ active: true, resetToken: this.state.resetToken }); + } - const reset = (animate = true) => { - if (props.onReset) props.onReset(); + reset(animate = true) { + if (this.props.onReset) this.props.onReset(); if (animate) { - Animated.spring(anim.current, { toValue: 0 }).start(); + Animated.spring(this.anim, { toValue: 0, useNativeDriver: false }).start(); } else { - anim.current.setValue(0); + this.anim.setValue(0); } - timeout.current = setTimeout(() => { - if (mounted.current) { - setActive(false); + setTimeout(() => { + if (this.mounted) { + this.setState({ active: false, resetToken: this.state.resetToken }); } }, 250); - }; - - return ( - - - {props.backdrop} - - - {active && !props.backgroundTappable && _renderTappableBackground()} - - {props.verticalOrientation === "up" && - props.children && - _renderActions()} - {_renderMainButton()} - {props.verticalOrientation === "down" && - props.children && - _renderActions()} - - - ); -}; + } +} ActionButton.Item = ActionButtonItem; @@ -407,4 +435,3 @@ const styles = StyleSheet.create({ backgroundColor: "transparent" } }); -export default ActionButton;