diff --git a/front/components/UI/ButtonBase.tsx b/front/components/UI/ButtonBase.tsx index 9807d0d..41032e0 100644 --- a/front/components/UI/ButtonBase.tsx +++ b/front/components/UI/ButtonBase.tsx @@ -1,6 +1,9 @@ -import React, { useRef, useState } from 'react'; -import { Animated, StyleSheet, TouchableOpacity, Text, ActivityIndicator, View, Image } from 'react-native'; +import React, { useState } from 'react'; +import { StyleSheet, ActivityIndicator, View, Image } from 'react-native'; import Ionicons from '@expo/vector-icons/Ionicons'; +import InteractiveBase from './InteractiveBase'; +import { Text, useTheme } from 'native-base'; +// import { BlurView } from 'expo-blur'; interface ButtonProps { title?: string; @@ -9,139 +12,154 @@ interface ButtonProps { icon?: string; iconImage?: string; isCollapsed?: boolean; - isOutlined?: boolean; + type: 'filled' | 'outlined' | 'menu' | 'submenu'; } -const ButtonBase: React.FC = ({ title, onPress, isDisabled, icon, iconImage, isCollapsed, isOutlined = false }) => { - const shouldCollapse = isCollapsed !== undefined ? isCollapsed : (!title && (icon || iconImage)); +const ButtonBase: React.FC = ({ title, onPress, isDisabled, icon, iconImage, isCollapsed, type = 'filled'}) => { + const { colors } = useTheme(); const [loading, setLoading] = useState(false); - const scaleValue = useRef(new Animated.Value(1)).current; - const colorValue = useRef(new Animated.Value(0)).current; - const backgroundColor = colorValue.interpolate({ - inputRange: [0, 1], - outputRange: isOutlined ? ['transparent', 'transparent'] : ['#6075F9', '#4352ae'], - }); - const borderColor = colorValue.interpolate({ - inputRange: [0, 1], - outputRange: ['#6075F9', '#4352ae'], - }); - - const textColor = colorValue.interpolate({ - inputRange: [0, 1], - outputRange: isOutlined ? ['#6075F9', '#4352ae'] : ['#ffffff', '#ffffff'], - }); - - // scale animation onClick - const handlePressIn = () => { - Animated.spring(scaleValue, { - toValue: 0.98, - useNativeDriver: true, - }).start(); - }; - - // scale animation reset after onClick - const handlePressOut = async () => { - Animated.spring(scaleValue, { - toValue: 1, - friction: 30, - // tension: 50, - useNativeDriver: true, - }).start(); - - if (onPress && !isDisabled) { - setLoading(true); - await onPress(); - setLoading(false); + const styleButton = StyleSheet.create({ + Default: { + scale: 1, + shadowOpacity: 0.30, + shadowRadius: 4.65, + elevation: 8, + backgroundColor: colors.primary[400], + }, + onHover: { + scale: 1.02, + shadowOpacity: 0.37, + shadowRadius: 7.49, + elevation: 12, + backgroundColor: colors.primary[500], + }, + onPressed: { + scale: 0.98, + shadowOpacity: 0.23, + shadowRadius: 2.62, + elevation: 4, + backgroundColor: colors.primary[600], + }, + Disabled: { + scale: 1, + shadowOpacity: 0.30, + shadowRadius: 4.65, + elevation: 8, + backgroundColor: colors.primary[400], } - }; + }); - // color animation onHover - const handleMouseEnter = () => { - Animated.timing(colorValue, { - toValue: 1, - duration: 250, - useNativeDriver: false, - }).start(); - }; + const styleMenu = StyleSheet.create({ + Default: { + scale: 1, + shadowOpacity: 0.30, + shadowRadius: 4.65, + elevation: 8, + backgroundColor: '#ff0000', + }, + onHover: { + scale: 1.01, + shadowOpacity: 0.37, + shadowRadius: 7.49, + elevation: 12, + backgroundColor: '#0ff000', + }, + onPressed: { + scale: 0.99, + shadowOpacity: 0.23, + shadowRadius: 2.62, + elevation: 4, + backgroundColor: '#000', + }, + Disabled: { + scale: 1, + shadowOpacity: 0.30, + shadowRadius: 4.65, + elevation: 8, + backgroundColor: '#0000', + } + }); - // color animation reset after onHover - const handleMouseLeave = () => { - Animated.timing(colorValue, { - toValue: 0, - duration: 250, - useNativeDriver: false, - }).start(); - }; + const typeToStyleAnimator = {'filled': styleButton,'outlined': styleButton,'menu': styleMenu,'submenu': styleButton}; return ( - - - - {loading ? ( - - ) : ( - - {icon && } - {iconImage && } - {title && {title}} - - )} - - - + { + if (onPress && !isDisabled) { + setLoading(true); + await onPress(); + setLoading(false); + } + }} + isDisabled={isDisabled} + isOutlined={type === 'outlined'} + > + {loading ? ( + + ) : ( + + {icon && } + {iconImage && } + {title && {title}} + + )} + ); }; +const styleAnimate = StyleSheet.create({ + Default: { + scale: 1, + shadowOpacity: 0.30, + shadowRadius: 4.65, + elevation: 8, + backgroundColor: '#00ff00', + }, + onHover: { + scale: 1.01, + shadowOpacity: 0.37, + shadowRadius: 7.49, + elevation: 12, + backgroundColor: '#0000ff', + }, + onPressed: { + scale: 0.99, + shadowOpacity: 0.23, + shadowRadius: 2.62, + elevation: 4, + backgroundColor: '#ff0000', + }, + Disabled: { + scale: 1, + shadowOpacity: 0.30, + shadowRadius: 4.65, + elevation: 8, + backgroundColor: '#000000', + } +}); + const styles = StyleSheet.create({ container: { - width: '100%', + borderRadius: 8, }, - collapsedContainer: { - width: 'fit-content', - // alignItems: 'center', - }, - buttonContainer: { - borderRadius: 16, - }, - button: { - borderRadius: 16, - padding: 16, + content: { + padding: 10, justifyContent: 'center', + flexDirection: 'row', alignItems: 'center', }, + icon: { + width: 18, + height: 18, + // marginRight: 8, + }, text: { color: '#fff', marginHorizontal: 8 }, - content: { - flexDirection: 'row', - alignItems: 'center', - }, - icon: { - width: 24, - height: 24, - // marginRight: 8, - }, }); export default ButtonBase; diff --git a/front/components/UI/InteractiveBase.tsx b/front/components/UI/InteractiveBase.tsx new file mode 100644 index 0000000..8dddbf7 --- /dev/null +++ b/front/components/UI/InteractiveBase.tsx @@ -0,0 +1,245 @@ +import React, { useRef, useState } from 'react'; +import { Animated, StyleSheet, TouchableOpacity, ActivityIndicator, View, Image, StyleProp, ViewStyle } from 'react-native'; +import Ionicons from '@expo/vector-icons/Ionicons'; +import { Text, useTheme } from 'native-base' +import { BlurView } from 'expo-blur'; + +import { + Fill, + BackdropBlur, +} from "@shopify/react-native-skia"; + +interface InteractiveBaseProps { + children?: React.ReactNode; + onPress?: () => Promise; + isDisabled?: boolean; + isOutlined?: boolean; + style?: StyleProp, + 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 = ({ children, onPress, style, styleAnimate, isDisabled = false, isOutlined = false }) => { + 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: true, + }), + 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: true, + }), + 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 = async () => { + Animated.parallel([ + Animated.spring(scaleAnimator, { + toValue: 1, + useNativeDriver: true, + }), + 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(); + + if (onPress && !isDisabled) { + await onPress(); + } + } + // Mouse Leave + const handleMouseLeave = () => { + Animated.parallel([ + Animated.spring(scaleAnimator, { + toValue: 0, + useNativeDriver: true, + }), + 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: true, + }), + ]).start(); + } + + return ( + + + {/* + {children} + */} + {children} + + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + width: '100%', + height: '100%', + }, +}); + +export default InteractiveBase; \ No newline at end of file diff --git a/front/package.json b/front/package.json index 11dcd15..4f8a073 100644 --- a/front/package.json +++ b/front/package.json @@ -25,6 +25,7 @@ "@react-navigation/native": "^6.0.11", "@react-navigation/native-stack": "^6.7.0", "@reduxjs/toolkit": "^1.8.3", + "@shopify/react-native-skia": "^0.1.208", "@tanstack/react-query": "^4.2.3", "@types/jest": "^28.1.4", "@types/react-dom": "~18.0.8", @@ -33,7 +34,9 @@ "add": "^2.0.6", "expo": "^47.0.8", "expo-asset": "~8.7.0", + "expo-blur": "~12.0.1", "expo-dev-client": "~2.0.1", + "expo-linear-gradient": "~12.0.1", "expo-linking": "~3.3.1", "expo-screen-orientation": "~5.0.1", "expo-secure-store": "~12.0.0", diff --git a/front/views/SigninView.tsx b/front/views/SigninView.tsx index 33c88e8..dd25d9b 100644 --- a/front/views/SigninView.tsx +++ b/front/views/SigninView.tsx @@ -9,13 +9,15 @@ import TextButton from '../components/TextButton'; import { useNavigation } from '../Navigation'; import { string } from 'yup'; import { FormControl, Input, Stack, Center, Button, Text, Box, useToast } from 'native-base'; -import { TouchableOpacity, Linking, View } from 'react-native' +import { TouchableOpacity, Linking, View, StyleSheet } from 'react-native' import TextFormField from '../components/UI/TextFormField'; import LinkBase from '../components/UI/LinkBase'; import SeparatorBase from '../components/UI/SeparatorBase'; import ButtonBase from '../components/UI/ButtonBase'; import { Image, Flex } from 'native-base'; import ImageBanner from '../assets/banner.jpg'; +import TMPBase from '../components/UI/TMPBase'; +import { LinearGradient } from 'expo-linear-gradient'; const hanldeSignin = async ( username: string, @@ -76,10 +78,15 @@ const SigninView = () => { Continuez avec Google ou entrez vos coordonnées. + or { { style={{width: '100%', height: '100%', borderRadius: 8}} /> + ); }; -export default SigninView; +export default SigninView; \ No newline at end of file diff --git a/front/yarn.lock b/front/yarn.lock index 65b862c..5dc4ccb 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -3549,6 +3549,14 @@ component-type "^1.2.1" join-component "^1.1.0" +"@shopify/react-native-skia@^0.1.208": + version "0.1.208" + resolved "https://registry.yarnpkg.com/@shopify/react-native-skia/-/react-native-skia-0.1.208.tgz#f3badb065dc0ed4bbf34a6036c9aa3ded2bfd068" + integrity sha512-5Zi6gYlyGo10A1hz2u8RnVP/xzOuRHRAo/HdOY0ncMyKSyvaNtINNfM7zuthRKZT4R3zFF38dYphfC7mcFELtQ== + dependencies: + canvaskit-wasm "0.38.0" + react-reconciler "^0.27.0" + "@sideway/address@^4.1.3": version "4.1.4" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" @@ -6897,6 +6905,11 @@ caniuse-lite@^1.0.30001125: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz#64a0ccef1911a9dcff647115b4430f8eff1ef2d9" integrity sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg== +canvaskit-wasm@0.38.0: + version "0.38.0" + resolved "https://registry.yarnpkg.com/canvaskit-wasm/-/canvaskit-wasm-0.38.0.tgz#83e6c46f3015c2ff3f6503157f47453af76a7be7" + integrity sha512-ZEG6lucpbQ4Ld+mY8C1Ng+PMLVP+/AX02jS0Sdl28NyMxuKSa9uKB8oGd1BYp1XWPyO2Jgr7U8pdyjJ/F3xR5Q== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -9056,6 +9069,11 @@ expo-asset@~8.7.0: path-browserify "^1.0.0" url-parse "^1.5.9" +expo-blur@~12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/expo-blur/-/expo-blur-12.0.1.tgz#7aa4186620359acfa976dda84360070b634ffe3d" + integrity sha512-7oF/xRIFJukM4/qL6ejZ4Z/4YcVExvBPsBrz7rGYz6PtgAkWwYFR62+ExZOzTEG4hgoPPmlnt1ncimsk/MYUgQ== + expo-constants@~14.0.0, expo-constants@~14.0.2: version "14.0.2" resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-14.0.2.tgz#2cb1dec8f41a64c2fc5b4eecaf77d7661cad01cc" @@ -9126,6 +9144,11 @@ expo-keep-awake@~11.0.1: resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-11.0.1.tgz#ee354465892a94040ffe09901b85b469e7d54fb3" integrity sha512-44ZjgLE4lnce2d40Pv8xsjMVc6R5GvgHOwZfkLYtGmgYG9TYrEJeEj5UfSeweXPL3pBFhXKfFU8xpGYMaHdP0A== +expo-linear-gradient@~12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/expo-linear-gradient/-/expo-linear-gradient-12.0.1.tgz#452f793b0463ddf313aad431552f23acc85f5d64" + integrity sha512-TMl/wBTVQOliL4S3DS5Aa3UFfVySr0mdJEHLG6kfBdMCLkr+tfLI2rGyJ+scS7xgMsvhTIaurhf1+Z0sL3aLCg== + expo-linking@~3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-3.3.1.tgz#253b183321e54cb6fa1a667a53d4594aa88a3357" @@ -15911,6 +15934,14 @@ react-query@*: broadcast-channel "^3.4.1" match-sorter "^6.0.2" +react-reconciler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b" + integrity sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.21.0" + react-redux@^8.0.2: version "8.0.5" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.5.tgz#e5fb8331993a019b8aaf2e167a93d10af469c7bd" @@ -16612,6 +16643,13 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" + integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== + dependencies: + loose-envify "^1.1.0" + scheduler@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8"