Files
Chromacase/front/components/UI/InteractiveBase.tsx
2024-01-09 17:34:35 +01:00

259 lines
6.2 KiB
TypeScript

import { Pressable, useTheme } from 'native-base';
import React, { useRef } from 'react';
import { Animated, StyleProp, ViewStyle } from 'react-native';
interface InteractiveBaseProps {
children?: React.ReactNode;
onPress?: () => Promise<void>;
onLongPress?: () => Promise<void>;
isDisabled?: boolean;
isOutlined?: boolean;
focusable?: boolean;
style?: StyleProp<ViewStyle>;
styleAnimate: {
Default: {
scale: number;
shadowOpacity: number;
shadowRadius: number;
elevation: number;
backgroundColor: string;
};
onHover: {
scale: number;
shadowOpacity: number;
shadowRadius: number;
elevation: number;
backgroundColor: string;
};
onPressed: {
scale: number;
shadowOpacity: number;
shadowRadius: number;
elevation: number;
backgroundColor: string;
};
Disabled: {
scale: number;
shadowOpacity: number;
shadowRadius: number;
elevation: number;
backgroundColor: string;
};
};
}
const InteractiveBase: React.FC<InteractiveBaseProps> = ({
children,
onPress,
onLongPress,
style,
styleAnimate,
isDisabled = false,
isOutlined = false,
focusable = true,
}) => {
const { colors } = useTheme();
const scaleAnimator = useRef(new Animated.Value(1)).current;
const scaleValue = scaleAnimator.interpolate({
inputRange: [0, 1, 2],
outputRange: [
styleAnimate.Default.scale,
styleAnimate.onHover.scale,
styleAnimate.onPressed.scale,
],
});
const shadowOpacityAnimator = useRef(new Animated.Value(0)).current;
const shadowOpacityValue = shadowOpacityAnimator.interpolate({
inputRange: [0, 1, 2],
outputRange: [
styleAnimate.Default.shadowOpacity,
styleAnimate.onHover.shadowOpacity,
styleAnimate.onPressed.shadowOpacity,
],
});
const shadowRadiusAnimator = useRef(new Animated.Value(0)).current;
const shadowRadiusValue = shadowRadiusAnimator.interpolate({
inputRange: [0, 1, 2],
outputRange: [
styleAnimate.Default.shadowRadius,
styleAnimate.onHover.shadowRadius,
styleAnimate.onPressed.shadowRadius,
],
});
const elevationAnimator = useRef(new Animated.Value(0)).current;
const elevationValue = elevationAnimator.interpolate({
inputRange: [0, 1, 2],
outputRange: [
styleAnimate.Default.elevation,
styleAnimate.onHover.elevation,
styleAnimate.onPressed.elevation,
],
});
const backgroundColorAnimator = useRef(new Animated.Value(0)).current;
const backgroundColorValue = backgroundColorAnimator.interpolate({
inputRange: [0, 1, 2],
outputRange: [
styleAnimate.Default.backgroundColor,
styleAnimate.onHover.backgroundColor,
styleAnimate.onPressed.backgroundColor,
],
});
// Mouse Enter
const handleMouseEnter = () => {
Animated.parallel([
Animated.spring(scaleAnimator, {
toValue: 1,
useNativeDriver: false,
}),
Animated.timing(backgroundColorAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowRadiusAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowOpacityAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(elevationAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
]).start();
};
// Mouse Down
const handlePressIn = () => {
Animated.parallel([
Animated.spring(scaleAnimator, {
toValue: 2,
useNativeDriver: false,
}),
Animated.timing(backgroundColorAnimator, {
toValue: 2,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowRadiusAnimator, {
toValue: 2,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowOpacityAnimator, {
toValue: 2,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(elevationAnimator, {
toValue: 2,
duration: 250,
useNativeDriver: false,
}),
]).start();
};
// Mouse Up
const handlePressOut = () => {
Animated.parallel([
Animated.spring(scaleAnimator, {
toValue: 1,
useNativeDriver: false,
}),
Animated.timing(backgroundColorAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowRadiusAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowOpacityAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(elevationAnimator, {
toValue: 1,
duration: 250,
useNativeDriver: false,
}),
]).start();
};
// Mouse Leave
const handleMouseLeave = () => {
Animated.parallel([
Animated.spring(scaleAnimator, {
toValue: 0,
useNativeDriver: false,
}),
Animated.timing(backgroundColorAnimator, {
toValue: 0,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowRadiusAnimator, {
toValue: 0,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(shadowOpacityAnimator, {
toValue: 0,
duration: 250,
useNativeDriver: false,
}),
Animated.timing(elevationAnimator, {
toValue: 0,
duration: 250,
useNativeDriver: false,
}),
]).start();
};
const animatedStyle = {
backgroundColor: isOutlined ? colors.coolGray[100] : backgroundColorValue,
borderColor: isOutlined ? backgroundColorValue : 'transparent',
borderWidth: 2,
transform: [{ scale: scaleValue }],
shadowOpacity: shadowOpacityValue,
shadowRadius: shadowRadiusValue,
elevation: elevationValue,
};
const disableStyle = {
backgroundColor: isOutlined ? colors.coolGray[100] : styleAnimate.Disabled.backgroundColor,
borderColor: isOutlined ? styleAnimate.Disabled.backgroundColor : 'transparent',
borderWidth: 2,
scale: styleAnimate.Disabled.scale,
shadowOpacity: styleAnimate.Disabled.shadowOpacity,
shadowRadius: styleAnimate.Disabled.shadowRadius,
elevation: styleAnimate.Disabled.elevation,
};
return (
<Animated.View style={[style, isDisabled ? disableStyle : animatedStyle]}>
<Pressable
focusable={focusable}
tabIndex={focusable ? 0 : -1}
disabled={isDisabled}
onHoverIn={handleMouseEnter}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
onHoverOut={handleMouseLeave}
onPress={onPress}
onLongPress={onLongPress}
>
{children}
</Pressable>
</Animated.View>
);
};
export default InteractiveBase;