Compare commits
1 Commits
main
...
feat/desig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d45a6b934 |
128
front/components/UI/AnimatedBase.tsx
Normal file
128
front/components/UI/AnimatedBase.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import React, { useRef, useEffect } from 'react';
|
||||||
|
import { Animated, StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
type StyleObject = Record<string, any>;
|
||||||
|
type InterpolatedStyleObject = Record<string, Animated.AnimatedInterpolation<any>>;
|
||||||
|
|
||||||
|
interface AnimatedBaseProps {
|
||||||
|
style?: StyleObject;
|
||||||
|
defaultStyle: StyleObject;
|
||||||
|
hoverStyle: StyleObject;
|
||||||
|
pressStyle: StyleObject;
|
||||||
|
currentState: number;
|
||||||
|
duration?: number;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
styleContainer?: StyleProp<ViewStyle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimatedBase: React.FC<AnimatedBaseProps> = ({
|
||||||
|
style,
|
||||||
|
defaultStyle,
|
||||||
|
hoverStyle,
|
||||||
|
pressStyle,
|
||||||
|
currentState,
|
||||||
|
children,
|
||||||
|
duration = 250,
|
||||||
|
styleContainer,
|
||||||
|
}) => {
|
||||||
|
const animatedValues = useRef<Record<string, Animated.Value>>({}).current;
|
||||||
|
|
||||||
|
const extractTransformKeys = (styleObject: StyleObject) => {
|
||||||
|
return styleObject.transform
|
||||||
|
? styleObject.transform.map((t: any) => Object.keys(t)[0])
|
||||||
|
: [];
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const allStyleKeys = new Set([
|
||||||
|
...Object.keys(defaultStyle),
|
||||||
|
...Object.keys(hoverStyle),
|
||||||
|
...Object.keys(pressStyle),
|
||||||
|
]);
|
||||||
|
|
||||||
|
allStyleKeys.forEach((key) => {
|
||||||
|
if (!animatedValues[key]) {
|
||||||
|
animatedValues[key] = new Animated.Value(0);
|
||||||
|
console.log('key; ', key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const allTransformKeys = new Set([
|
||||||
|
...extractTransformKeys(defaultStyle),
|
||||||
|
...extractTransformKeys(hoverStyle),
|
||||||
|
...extractTransformKeys(pressStyle),
|
||||||
|
]);
|
||||||
|
|
||||||
|
allTransformKeys.forEach((key) => {
|
||||||
|
if (!animatedValues[key]) {
|
||||||
|
animatedValues[key] = new Animated.Value(0);
|
||||||
|
console.log('keyxx; ', key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [defaultStyle, hoverStyle, pressStyle]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
animateToState(currentState);
|
||||||
|
}, [currentState]);
|
||||||
|
|
||||||
|
const getTransformValue = (key: string, style: StyleObject) => {
|
||||||
|
const transformObject = style.transform?.find((t: any) => t.hasOwnProperty(key));
|
||||||
|
return transformObject ? transformObject[key] : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const interpolateStyle = (stateStyle: StyleObject): InterpolatedStyleObject => {
|
||||||
|
const interpolatedStyle: InterpolatedStyleObject = {};
|
||||||
|
const transform: any = [];
|
||||||
|
|
||||||
|
Object.keys(animatedValues).forEach((key) => {
|
||||||
|
if (stateStyle.transform?.some((t: any) => t.hasOwnProperty(key))) {
|
||||||
|
const defaultValue = getTransformValue(key, defaultStyle);
|
||||||
|
const hoverValue = getTransformValue(key, hoverStyle);
|
||||||
|
const pressValue = getTransformValue(key, pressStyle);
|
||||||
|
|
||||||
|
const interpolated = animatedValues[key]!.interpolate({
|
||||||
|
inputRange: [0, 1, 2],
|
||||||
|
outputRange: [defaultValue, hoverValue, pressValue],
|
||||||
|
});
|
||||||
|
|
||||||
|
transform.push({ [key]: interpolated });
|
||||||
|
} else if (stateStyle[key]) {
|
||||||
|
const defaultValue = defaultStyle[key] || 0;
|
||||||
|
const hoverValue = hoverStyle[key] !== undefined ? hoverStyle[key] : defaultValue;
|
||||||
|
const pressValue = pressStyle[key] !== undefined ? pressStyle[key] : defaultValue;
|
||||||
|
|
||||||
|
interpolatedStyle[key] = animatedValues[key]!.interpolate({
|
||||||
|
inputRange: [0, 1, 2],
|
||||||
|
outputRange: [defaultValue, hoverValue, pressValue],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (transform.length > 0) {
|
||||||
|
interpolatedStyle.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
return interpolatedStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
const animateToState = (stateValue: number) => {
|
||||||
|
Object.keys(animatedValues).forEach((key) => {
|
||||||
|
Animated.timing(animatedValues[key]!, {
|
||||||
|
toValue: stateValue,
|
||||||
|
duration: duration,
|
||||||
|
useNativeDriver: false,
|
||||||
|
}).start();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const animatedStyle = StyleSheet.flatten([
|
||||||
|
styleContainer,
|
||||||
|
interpolateStyle(defaultStyle),
|
||||||
|
interpolateStyle(hoverStyle),
|
||||||
|
interpolateStyle(pressStyle),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return <Animated.View style={[style, animatedStyle]}>{children && children}</Animated.View>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AnimatedBase;
|
||||||
34
front/components/UI/InteractiveBaseV2.tsx
Normal file
34
front/components/UI/InteractiveBaseV2.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Pressable } from 'react-native';
|
||||||
|
|
||||||
|
interface InteractiveBaseProps {
|
||||||
|
handleHoverIn: () => void;
|
||||||
|
handleHoverOut: () => void;
|
||||||
|
handlePressIn: () => void;
|
||||||
|
handlePressOut: () => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
style?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InteractiveBase: React.FC<InteractiveBaseProps> = ({
|
||||||
|
handleHoverIn,
|
||||||
|
handleHoverOut,
|
||||||
|
handlePressIn,
|
||||||
|
handlePressOut,
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
style={style}
|
||||||
|
onHoverIn={handleHoverIn}
|
||||||
|
onPressIn={handlePressIn}
|
||||||
|
onPressOut={handlePressOut}
|
||||||
|
onHoverOut={handleHoverOut}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InteractiveBase;
|
||||||
@@ -5,19 +5,6 @@ import { Animated, StyleSheet, Pressable, ViewStyle, StyleProp } from 'react-nat
|
|||||||
type StyleObject = Record<string, any>;
|
type StyleObject = Record<string, any>;
|
||||||
type InterpolatedStyleObject = Record<string, Animated.AnimatedInterpolation<any>>;
|
type InterpolatedStyleObject = Record<string, Animated.AnimatedInterpolation<any>>;
|
||||||
|
|
||||||
const TRANSFORM_WHITELIST = {
|
|
||||||
translateX: true,
|
|
||||||
translateY: true,
|
|
||||||
scale: true,
|
|
||||||
scaleX: true,
|
|
||||||
scaleY: true,
|
|
||||||
rotate: true,
|
|
||||||
rotateX: true,
|
|
||||||
rotateY: true,
|
|
||||||
rotateZ: true,
|
|
||||||
perspective: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface InteractiveCCProps {
|
interface InteractiveCCProps {
|
||||||
defaultStyle: StyleObject;
|
defaultStyle: StyleObject;
|
||||||
hoverStyle: StyleObject;
|
hoverStyle: StyleObject;
|
||||||
|
|||||||
48
front/components/UI/useInteractionState.tsx
Normal file
48
front/components/UI/useInteractionState.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { useState, useCallback } from 'react';
|
||||||
|
|
||||||
|
const InteractionStates = {
|
||||||
|
NORMAL: 0,
|
||||||
|
HOVER: 1,
|
||||||
|
PRESSED: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface InteractionStateProps {
|
||||||
|
onHoverIn?: () => void;
|
||||||
|
onHoverOut?: () => void;
|
||||||
|
onPressIn?: () => void;
|
||||||
|
onPressOut?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useInteractionState = ({ onHoverIn, onHoverOut, onPressIn, onPressOut }: InteractionStateProps = {}) => {
|
||||||
|
const [state, setState] = useState(InteractionStates.NORMAL);
|
||||||
|
|
||||||
|
const handleHoverIn = useCallback(() => {
|
||||||
|
setState(InteractionStates.HOVER);
|
||||||
|
if (onHoverIn) onHoverIn();
|
||||||
|
}, [onHoverIn]);
|
||||||
|
|
||||||
|
const handleHoverOut = useCallback(() => {
|
||||||
|
setState(InteractionStates.NORMAL);
|
||||||
|
if (onHoverOut) onHoverOut();
|
||||||
|
}, [onHoverOut]);
|
||||||
|
|
||||||
|
const handlePressIn = useCallback(() => {
|
||||||
|
setState(InteractionStates.PRESSED);
|
||||||
|
if (onPressIn) onPressIn();
|
||||||
|
}, [onPressIn]);
|
||||||
|
|
||||||
|
const handlePressOut = useCallback(() => {
|
||||||
|
setState(InteractionStates.HOVER);
|
||||||
|
if (onPressOut) onPressOut();
|
||||||
|
}, [onPressOut]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
handleHoverIn,
|
||||||
|
handleHoverOut,
|
||||||
|
handlePressIn,
|
||||||
|
handlePressOut,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useInteractionState;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useBreakpointValue, useTheme } from 'native-base';
|
import { View, useBreakpointValue, useTheme, Text } from 'native-base';
|
||||||
import { useWindowDimensions } from 'react-native';
|
import { StyleProp, ViewStyle, useWindowDimensions } from 'react-native';
|
||||||
import {
|
import {
|
||||||
TabView,
|
TabView,
|
||||||
SceneMap,
|
SceneMap,
|
||||||
@@ -20,6 +20,68 @@ import API from '../API';
|
|||||||
import { LoadingView } from '../components/Loading';
|
import { LoadingView } from '../components/Loading';
|
||||||
import { useLikeSongMutation } from '../utils/likeSongMutation';
|
import { useLikeSongMutation } from '../utils/likeSongMutation';
|
||||||
import Song from '../models/Song';
|
import Song from '../models/Song';
|
||||||
|
import InteractiveCC from '../components/UI/InteractiveCC';
|
||||||
|
import ButtonBase from '../components/UI/ButtonBase';
|
||||||
|
import InteractiveBase from '../components/UI/InteractiveBaseV2';
|
||||||
|
import AnimatedBase from '../components/UI/AnimatedBase';
|
||||||
|
import useInteractionState from '../components/UI/useInteractionState';
|
||||||
|
|
||||||
|
// import React from 'react';
|
||||||
|
// import { Text, View } from 'react-native';
|
||||||
|
// import InteractiveBase from './InteractiveBase';
|
||||||
|
// import AnimatedBase from './AnimatedBase';
|
||||||
|
// import useInteractionState from './useInteractionState';
|
||||||
|
|
||||||
|
interface LinkBaseProps {
|
||||||
|
text: string;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
|
textStyle?: StyleProp<ViewStyle>;
|
||||||
|
underlineStyle?: StyleProp<ViewStyle>;
|
||||||
|
fontSize?: number;
|
||||||
|
onPress: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimatedLink = ({ text, style, textStyle, underlineStyle, fontSize = 14 }: LinkBaseProps) => {
|
||||||
|
const interaction = useInteractionState({
|
||||||
|
onPressOut: () => { console.log("AnimatedLink is activate")}
|
||||||
|
});
|
||||||
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
const defaultUnderlineStyle = { height: fontSize / 8, bottom: 0 };
|
||||||
|
const hoverUnderlineStyle = { height: fontSize * 1.5, bottom: 0 };
|
||||||
|
const pressUnderlineStyle = { height: 0, bottom: fontSize * 1.5 };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, alignItems: 'flex-start', position: 'relative'}}>
|
||||||
|
<InteractiveBase {...interaction} style={style}>
|
||||||
|
<AnimatedBase
|
||||||
|
defaultStyle={{ fontSize: 14 }}
|
||||||
|
hoverStyle={{ fontSize: 16 }}
|
||||||
|
pressStyle={{ fontSize: 8 }}
|
||||||
|
currentState={interaction.state}
|
||||||
|
>
|
||||||
|
<Text selectable={false} style={[textStyle]}>
|
||||||
|
{/* {fontSize: fontSize}, */}
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</AnimatedBase>
|
||||||
|
<AnimatedBase
|
||||||
|
style={[{
|
||||||
|
minWidth: '100%',
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: -1,
|
||||||
|
backgroundColor: colors.primary[600],
|
||||||
|
}, underlineStyle && {underlineStyle}]}
|
||||||
|
defaultStyle={{ ...defaultUnderlineStyle }}
|
||||||
|
hoverStyle={{ ...hoverUnderlineStyle }}
|
||||||
|
pressStyle={{ ...pressUnderlineStyle }}
|
||||||
|
currentState={interaction.state}
|
||||||
|
/>
|
||||||
|
</InteractiveBase>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
type MusicListCCProps = {
|
type MusicListCCProps = {
|
||||||
data: Song[] | undefined;
|
data: Song[] | undefined;
|
||||||
@@ -58,12 +120,81 @@ const MusicListCC = ({ data, isLoading, refetch }: MusicListCCProps) => {
|
|||||||
|
|
||||||
const FavoritesMusic = () => {
|
const FavoritesMusic = () => {
|
||||||
const likedSongs = useQuery(API.getLikedSongs(['artist', 'SongHistory', 'likedByUsers']));
|
const likedSongs = useQuery(API.getLikedSongs(['artist', 'SongHistory', 'likedByUsers']));
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const interaction = useInteractionState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MusicListCC
|
<>
|
||||||
data={likedSongs.data?.map((x) => x.song)}
|
<View style={{margin: 30}}>
|
||||||
isLoading={likedSongs.isLoading}
|
<AnimatedLink text="coucou Je suis un link zosidjofsijdfosijfosifdjo" onPress={() => console.log("Je suis le lien !!!")}/>
|
||||||
refetch={likedSongs.refetch}
|
<InteractiveBase {...interaction} style={{ marginTop: 20 }}>
|
||||||
/>
|
<AnimatedBase
|
||||||
|
defaultStyle={{
|
||||||
|
backgroundColor: colors.primary[300],
|
||||||
|
}}
|
||||||
|
hoverStyle={{
|
||||||
|
backgroundColor: colors.primary[900],
|
||||||
|
}}
|
||||||
|
pressStyle={{
|
||||||
|
backgroundColor: colors.primary[100],
|
||||||
|
}}
|
||||||
|
currentState={interaction.state}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
Text
|
||||||
|
</Text>
|
||||||
|
</AnimatedBase>
|
||||||
|
</InteractiveBase>
|
||||||
|
<InteractiveCC
|
||||||
|
// duration={80}
|
||||||
|
styleContainer={{
|
||||||
|
borderRadius: 10,
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
paddingVertical: 10,
|
||||||
|
// borderRadius: 10,
|
||||||
|
}}
|
||||||
|
defaultStyle={{
|
||||||
|
transform: [{ scale: 1,}],
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 4.65,
|
||||||
|
elevation: 8,
|
||||||
|
backgroundColor: colors.primary[300],
|
||||||
|
}}
|
||||||
|
hoverStyle={{
|
||||||
|
transform: [{ scale: 1.02,}],
|
||||||
|
shadowOpacity: 0.37,
|
||||||
|
shadowRadius: 7.49,
|
||||||
|
elevation: 12,
|
||||||
|
backgroundColor: colors.primary[400],
|
||||||
|
}}
|
||||||
|
pressStyle={{
|
||||||
|
transform: [{ scale: 0.98,}],
|
||||||
|
shadowOpacity: 0.23,
|
||||||
|
shadowRadius: 2.62,
|
||||||
|
elevation: 4,
|
||||||
|
backgroundColor: colors.primary[500],
|
||||||
|
}}
|
||||||
|
onPress={() => console.log("A que coucou!")}
|
||||||
|
>
|
||||||
|
<Text selectable={false} style={{color: '#fff'}}>
|
||||||
|
Coucou
|
||||||
|
</Text>
|
||||||
|
</InteractiveCC>
|
||||||
|
<ButtonBase
|
||||||
|
title="Coucou"
|
||||||
|
style={{ marginTop: 20 }}
|
||||||
|
type={'filled'}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<MusicListCC
|
||||||
|
data={likedSongs.data?.map((x) => x.song)}
|
||||||
|
isLoading={likedSongs.isLoading}
|
||||||
|
refetch={likedSongs.refetch}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user