react-native-really-awesome-button
Version:
React Native Button UI component that renders an 60fps animated set of progress enabled 3D performant buttons.
393 lines (391 loc) • 15.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _helpers = require("./helpers");
var _lodash = _interopRequireDefault(require("lodash.debounce"));
var _styles = require("./styles");
var _constants = require("./constants");
var _Placeholder = _interopRequireDefault(require("./Placeholder"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const AwesomeButton = _ref => {
let {
activityColor = _constants.DEFAULT_ACTIVITY_COLOR,
activeOpacity = _constants.DEFAULT_ACTIVE_OPACITY,
animatedPlaceholder = true,
backgroundActive = _constants.DEFAULT_BACKGROUND_ACTIVE,
backgroundColor = _constants.DEFAULT_BACKGROUND_COLOR,
backgroundDarker = _constants.DEFAULT_BACKGROUND_DARKER,
backgroundPlaceholder = _constants.DEFAULT_BACKGROUND_SHADOW,
backgroundProgress = _constants.DEFAULT_BACKGROUND_SHADOW,
backgroundShadow = _constants.DEFAULT_BACKGROUND_SHADOW,
borderColor,
borderRadius = _constants.DEFAULT_BORDER_RADIUS,
borderBottomLeftRadius,
borderBottomRightRadius,
borderTopLeftRadius,
borderTopRightRadius,
borderWidth = _constants.DEFAULT_BORDER_WIDTH,
children = null,
before = null,
after = null,
disabled = false,
height = _constants.DEFAULT_HEIGHT,
hitSlop = null,
debouncedPressTime = _constants.DEFAULT_DEBOUNCED_PRESS_TIME,
paddingHorizontal = _constants.DEFAULT_HORIZONTAL_PADDING,
onPress = () => null,
onPressIn = () => null,
onPressedIn = () => null,
onPressOut = () => null,
onPressedOut = () => null,
onProgressStart = () => null,
onProgressEnd = () => null,
onLongPress = null,
dangerouslySetPressableProps = {},
progress = false,
paddingBottom = 0,
paddingTop = 0,
progressLoadingTime = _constants.ANIMATED_TIMING_LOADING,
raiseLevel = _constants.DEFAULT_RAISE_LEVEL,
springRelease = true,
stretch = false,
style = null,
textColor = _constants.DEFAULT_TEXT_COLOR,
textLineHeight = _constants.DEFAULT_LINE_HEIGHT,
textSize = _constants.DEFAULT_TEXT_SIZE,
textFontFamily,
width = _constants.DEFAULT_WIDTH,
extra = null
} = _ref;
const loadingOpacity = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
const textOpacity = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
const activityOpacity = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const animatedActive = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const animatedValue = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const animatedLoading = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const animatedOpacity = (0, _react.useRef)(new _reactNative.Animated.Value(width === null && !stretch === true ? 0 : 1)).current;
const pressing = (0, _react.useRef)(false);
const pressed = (0, _react.useRef)(false);
const actioned = (0, _react.useRef)(false);
const progressing = (0, _react.useRef)(false);
const timeout = (0, _react.useRef)(null);
const containerWidth = (0, _react.useRef)(null);
const pressAnimation = (0, _react.useRef)(null);
const [activity, setActivity] = (0, _react.useState)(false);
const [stateWidth, setStateWidth] = (0, _react.useState)(null);
const debouncedPress = debouncedPressTime ? (0, _lodash.default)(animateProgressEnd => onPress(animateProgressEnd), debouncedPressTime, {
trailing: false,
leading: true
}) : onPress;
const layout = {
backgroundActive,
backgroundColor,
backgroundDarker,
backgroundPlaceholder,
backgroundProgress,
backgroundShadow,
borderColor,
borderRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
borderTopLeftRadius,
borderTopRightRadius,
borderWidth,
height,
paddingBottom,
paddingHorizontal,
paddingTop,
raiseLevel,
stateWidth,
stretch,
textColor,
textFontFamily,
textLineHeight,
textSize,
width
};
const dynamicStyles = (0, _react.useMemo)(() => {
return (0, _styles.getStyles)(layout);
}, [backgroundActive, backgroundColor, backgroundDarker, backgroundPlaceholder, backgroundProgress, backgroundShadow, borderColor, borderRadius, borderBottomLeftRadius, borderBottomRightRadius, borderTopLeftRadius, borderTopRightRadius, borderWidth, height, paddingBottom, paddingHorizontal, paddingTop, raiseLevel, stateWidth, stretch, textColor, textFontFamily, textLineHeight, textSize, width]);
const getAnimatedValues = () => {
let width = containerWidth.current ? containerWidth.current * -1 : 0;
return {
animatedContainer: {
opacity: animatedOpacity
},
animatedShadow: {
transform: [{
translateY: animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, -raiseLevel / 2]
})
}]
},
animatedContent: {
transform: [{
translateY: animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, raiseLevel]
})
}]
},
animatedActive: {
opacity: animatedActive
},
animatedActivity: {
opacity: activityOpacity,
transform: [{
scale: activityOpacity
}]
},
animatedProgress: {
opacity: loadingOpacity,
transform: [{
translateX: animatedLoading.interpolate({
inputRange: [0, 1],
outputRange: [width, 0]
})
}]
}
};
};
const onTextLayout = event => {
containerWidth.current = event.nativeEvent.layout.width;
animatedOpacity.setValue(1);
if (width !== null && stretch === false) {
return;
}
if (stateWidth !== event.nativeEvent.layout.width && (stateWidth === null || stateWidth < event.nativeEvent.layout.width)) {
setStateWidth(event.nativeEvent.layout.width);
}
};
const animatePressIn = (0, _react.useCallback)(() => {
pressing.current = true;
pressAnimation.current = _reactNative.Animated.parallel([(0, _helpers.animateTiming)({
variable: animatedValue,
toValue: 1,
duration: _constants.ANIMATED_TIMING_OFF
}), (0, _helpers.animateTiming)({
variable: animatedActive,
toValue: 1,
duration: _constants.ANIMATED_TIMING_OFF
}), (0, _helpers.animateTiming)({
variable: animatedOpacity,
toValue: progress ? 1 : activeOpacity,
duration: _constants.ANIMATED_TIMING_OFF
})]);
pressAnimation.current.start(() => {
pressed.current = true;
onPressedIn && onPressedIn();
});
}, []);
const animateLoadingStart = () => {
animatedLoading.setValue(0);
(0, _helpers.animateTiming)({
variable: animatedLoading,
toValue: 1,
duration: progressLoadingTime
}).start();
};
const animateContentOut = () => {
_reactNative.Animated.parallel([(0, _helpers.animateTiming)({
variable: loadingOpacity,
toValue: 1
}), (0, _helpers.animateElastic)({
variable: textOpacity,
toValue: 0
}), (0, _helpers.animateElastic)({
variable: activityOpacity,
toValue: 1
})]).start();
};
const animateProgressEnd = callback => {
if (progress !== true) {
return;
}
if (timeout !== null && timeout !== void 0 && timeout.current) {
clearTimeout(timeout.current);
}
requestAnimationFrame(() => {
(0, _helpers.animateTiming)({
variable: animatedLoading,
toValue: 1
}).start(() => {
_reactNative.Animated.parallel([(0, _helpers.animateElastic)({
variable: textOpacity,
toValue: 1
}), (0, _helpers.animateElastic)({
variable: activityOpacity,
toValue: 0
}), (0, _helpers.animateTiming)({
variable: loadingOpacity,
toValue: 0,
delay: 100
})]).start(() => {
animateRelease(() => {
progressing.current = false;
callback && callback();
onProgressEnd && onProgressEnd();
});
});
});
});
};
const animateRelease = callback => {
if (pressAnimation.current) {
pressAnimation.current.stop();
}
pressed.current = false;
pressing.current = false;
const end = () => {
pressed.current = false;
pressing.current = false;
callback && callback();
onPressedOut && onPressedOut();
};
if (springRelease === true) {
_reactNative.Animated.parallel([(0, _helpers.animateSpring)({
variable: animatedActive,
toValue: 0
}), (0, _helpers.animateSpring)({
variable: animatedValue,
toValue: 0
}), (0, _helpers.animateTiming)({
variable: animatedOpacity,
toValue: 1
})]).start(end);
return;
}
_reactNative.Animated.parallel([(0, _helpers.animateTiming)({
variable: animatedActive,
toValue: 0,
duration: _constants.ANIMATED_TIMING_OFF
}), (0, _helpers.animateTiming)({
variable: animatedValue,
toValue: 0,
duration: _constants.ANIMATED_TIMING_OFF
}), (0, _helpers.animateTiming)({
variable: animatedOpacity,
toValue: 1
})]).start(end);
};
const startProgress = () => {
progressing.current = true;
onProgressStart && onProgressStart();
setActivity(true);
animateLoadingStart();
animateContentOut();
};
const press = () => {
actioned.current = true;
if (progressing.current === true) {
return;
}
if (progress === true) {
requestAnimationFrame(startProgress);
}
debouncedPress(animateProgressEnd);
};
const handlePressIn = (0, _react.useCallback)(event => {
if (disabled === true || !children || progressing.current === true || pressed.current === true) {
return;
}
onPressIn && onPressIn(event);
animatePressIn();
}, [disabled, children, onPressIn]);
const handlePressOut = (0, _react.useCallback)(event => {
var _event$nativeEvent;
if (disabled === true || !children || progressing.current === true) {
return;
}
onPressOut && onPressOut(event);
// @ts-ignore
if (event !== null && event !== void 0 && (_event$nativeEvent = event.nativeEvent) !== null && _event$nativeEvent !== void 0 && _event$nativeEvent.contentOffset) {
animateRelease();
return;
}
if (pressing.current === true || raiseLevel === 0) {
press();
if (progress === true) {
return;
}
}
animateRelease();
}, [raiseLevel, children, progress, onPress]);
const animatedValues = getAnimatedValues();
const renderActivity = (0, _react.useMemo)(() => {
if (activity === false) {
return null;
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
testID: "aws-btn-progress",
style: [_styles.styles.progress, dynamicStyles.progress, animatedValues.animatedProgress]
}), /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
testID: "aws-btn-activity-indicator",
style: [_styles.styles.container__activity, animatedValues.animatedActivity]
}, /*#__PURE__*/_react.default.createElement(_reactNative.ActivityIndicator, {
color: activityColor
})));
}, [activity]);
const renderContent = (0, _react.useMemo)(() => {
const animatedStyles = {
opacity: textOpacity,
transform: [{
scale: textOpacity
}]
};
if (!children) {
return /*#__PURE__*/_react.default.createElement(_Placeholder.default, {
animated: animatedPlaceholder,
style: [dynamicStyles.container__placeholder]
});
}
let content = children;
if (typeof children === 'string') {
content = /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
testID: "aws-btn-content-text",
style: [_styles.styles.container__text, dynamicStyles.container__text]
}, children);
}
return /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
style: [_styles.styles.container__view, dynamicStyles.container__view, animatedStyles]
}, before, content, after);
}, [children, before, after, textColor]);
return /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, _extends({
testID: "aws-btn-content-view",
hitSlop: hitSlop,
onLongPress: onLongPress
}, dangerouslySetPressableProps, {
onPressIn: handlePressIn,
onPressOut: handlePressOut
}), /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
testID: "aws-btn-content-2",
style: [_styles.styles.container, dynamicStyles.container, animatedValues.animatedContainer, style]
}, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
testID: "aws-btn-shadow",
style: [_styles.styles.shadow, dynamicStyles.shadow, animatedValues.animatedShadow]
}), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
testID: "aws-btn-bottom",
style: [_styles.styles.bottom, dynamicStyles.bottom]
}), /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
testID: "aws-btn-content",
style: [_styles.styles.content, dynamicStyles.content, animatedValues.animatedContent]
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
testID: "aws-btn-text",
style: [_styles.styles.text, dynamicStyles.text],
onLayout: onTextLayout
}, extra, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
testID: "aws-btn-active-background",
style: [_styles.styles.activeBackground, dynamicStyles.activeBackground, animatedValues.animatedActive]
}), renderActivity, renderContent))));
};
var _default = AwesomeButton;
exports.default = _default;
//# sourceMappingURL=Button.js.map