animation, dots, dropdown, popover and design fixes
This commit is contained in:
@@ -6,6 +6,7 @@ import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
Animated,
|
||||
FlatList,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
@@ -15,6 +16,8 @@ import {
|
||||
import { ai } from "../../assets";
|
||||
import { getCreationStageStates } from "../../utils/projectStages";
|
||||
import PersonaCard from "../cards/PersonaCard/PersonaCard";
|
||||
import AnimatedPaginationDot from "../AnimatedPaginationDot/AnimatedPaginationDot";
|
||||
import { BlurView } from "expo-blur";
|
||||
|
||||
const STAGE_CARD_CONTENT = [
|
||||
{
|
||||
@@ -65,14 +68,14 @@ const FeatureCarousel = ({
|
||||
...item,
|
||||
isLocked: stageStates[index]?.isLocked ?? true,
|
||||
})),
|
||||
[stageStates]
|
||||
[stageStates],
|
||||
);
|
||||
|
||||
const { height: windowHeight } = useWindowDimensions();
|
||||
const isWeb = Platform.OS === "web";
|
||||
|
||||
const [viewportHeight, setViewportHeight] = useState(() =>
|
||||
Math.max(windowHeight, 1)
|
||||
Math.max(windowHeight, 1),
|
||||
);
|
||||
|
||||
const updateSnapHeight = useCallback((height) => {
|
||||
@@ -97,9 +100,10 @@ const FeatureCarousel = ({
|
||||
const pendingScrollRef = useRef(false);
|
||||
const alignTimeoutRef = useRef(null);
|
||||
const activeIndexRef = useRef(
|
||||
typeof activeIndex === "number" ? activeIndex : 0
|
||||
typeof activeIndex === "number" ? activeIndex : 0,
|
||||
);
|
||||
const onActiveIndexChangeRef = useRef(onActiveIndexChange);
|
||||
const scrollY = useRef(new Animated.Value(0)).current;
|
||||
|
||||
useEffect(() => {
|
||||
onActiveIndexChangeRef.current = onActiveIndexChange;
|
||||
@@ -124,7 +128,7 @@ const FeatureCarousel = ({
|
||||
}
|
||||
return index;
|
||||
},
|
||||
[carouselItems.length]
|
||||
[carouselItems.length],
|
||||
);
|
||||
|
||||
const clearPendingAlignment = useCallback(() => {
|
||||
@@ -169,7 +173,7 @@ const FeatureCarousel = ({
|
||||
pendingScrollRef.current = false;
|
||||
}
|
||||
},
|
||||
[clampIndex, isWeb, itemHeight, updateSnapHeight]
|
||||
[clampIndex, isWeb, itemHeight, updateSnapHeight],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -234,7 +238,7 @@ const FeatureCarousel = ({
|
||||
scrollToIndex(nextIndex, shouldAnimate, height);
|
||||
}
|
||||
},
|
||||
[clampIndex, isWeb, itemHeight, scrollToIndex, updateSnapHeight]
|
||||
[clampIndex, isWeb, itemHeight, scrollToIndex, updateSnapHeight],
|
||||
);
|
||||
|
||||
const handleScrollEnd = useCallback(
|
||||
@@ -244,7 +248,7 @@ const FeatureCarousel = ({
|
||||
const layoutHeight = event?.nativeEvent?.layoutMeasurement?.height;
|
||||
alignToOffset(offsetY, layoutHeight);
|
||||
},
|
||||
[alignToOffset, clearPendingAlignment]
|
||||
[alignToOffset, clearPendingAlignment],
|
||||
);
|
||||
|
||||
const handleScroll = useCallback(
|
||||
@@ -260,7 +264,16 @@ const FeatureCarousel = ({
|
||||
alignTimeoutRef.current = null;
|
||||
}, 80);
|
||||
},
|
||||
[alignToOffset, clearPendingAlignment, isWeb]
|
||||
[alignToOffset, clearPendingAlignment, isWeb],
|
||||
);
|
||||
|
||||
const animatedScrollHandler = useMemo(
|
||||
() =>
|
||||
Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
|
||||
useNativeDriver: false,
|
||||
listener: isWeb ? handleScroll : undefined,
|
||||
}),
|
||||
[handleScroll, isWeb, scrollY],
|
||||
);
|
||||
|
||||
const handleLayout = useCallback(
|
||||
@@ -270,7 +283,7 @@ const FeatureCarousel = ({
|
||||
updateSnapHeight(layoutHeight);
|
||||
}
|
||||
},
|
||||
[updateSnapHeight]
|
||||
[updateSnapHeight],
|
||||
);
|
||||
|
||||
const keyExtractor = useCallback((item) => item.key, []);
|
||||
@@ -284,7 +297,7 @@ const FeatureCarousel = ({
|
||||
height={itemHeight}
|
||||
/>
|
||||
),
|
||||
[itemHeight]
|
||||
[itemHeight],
|
||||
);
|
||||
|
||||
const getItemLayout = useCallback(
|
||||
@@ -293,7 +306,7 @@ const FeatureCarousel = ({
|
||||
offset: itemHeight * index,
|
||||
index,
|
||||
}),
|
||||
[itemHeight]
|
||||
[itemHeight],
|
||||
);
|
||||
|
||||
const viewabilityConfig = useRef({ viewAreaCoveragePercentThreshold: 60 });
|
||||
@@ -324,8 +337,20 @@ const FeatureCarousel = ({
|
||||
return carouselItems.map((_, index) => index * itemHeight);
|
||||
}, [carouselItems, isWeb, itemHeight]);
|
||||
|
||||
const blurIntensity = isWeb ? 80 : 30;
|
||||
const dotsWrapperStyle = useMemo(
|
||||
() => [
|
||||
styles.dotsWrapperBase,
|
||||
isWeb ? styles.dotsWrapperWeb : styles.dotsWrapperNative,
|
||||
],
|
||||
[isWeb],
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]} onLayout={handleLayout}>
|
||||
<View
|
||||
style={[styles.container, isWeb && styles.containerWeb, style]}
|
||||
onLayout={handleLayout}
|
||||
>
|
||||
<FlatList
|
||||
ref={listRef}
|
||||
data={carouselItems}
|
||||
@@ -348,10 +373,28 @@ const FeatureCarousel = ({
|
||||
disableIntervalMomentum={!isWeb}
|
||||
decelerationRate={!isWeb ? "fast" : undefined}
|
||||
style={styles.list}
|
||||
onScroll={isWeb ? handleScroll : undefined}
|
||||
onScroll={animatedScrollHandler}
|
||||
onMomentumScrollEnd={handleScrollEnd}
|
||||
onScrollEndDrag={isWeb ? handleScrollEnd : undefined}
|
||||
/>
|
||||
<BlurView
|
||||
intensity={blurIntensity}
|
||||
tint={Platform.OS === "web" ? undefined : "dark"}
|
||||
style={dotsWrapperStyle}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<AnimatedPaginationDot
|
||||
data={carouselItems}
|
||||
scrollValue={scrollY}
|
||||
itemDimension={itemHeight}
|
||||
orientation={AnimatedPaginationDot.orientation.VERTICAL}
|
||||
expandingDotSize={24}
|
||||
inactiveDotOpacity={0.3}
|
||||
containerStyle={styles.dotsContainer}
|
||||
dotStyle={styles.dot}
|
||||
baseDotSize={8}
|
||||
/>
|
||||
</BlurView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -362,8 +405,44 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
flexDirection: "row",
|
||||
},
|
||||
containerWeb: {
|
||||
paddingRight: 56,
|
||||
},
|
||||
list: {
|
||||
flex: 1,
|
||||
},
|
||||
dotsWrapperBase: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: 16,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 8,
|
||||
overflow: "hidden",
|
||||
backgroundColor: "rgba(18, 18, 18, 0.2)",
|
||||
},
|
||||
dotsWrapperWeb: {
|
||||
position: "absolute",
|
||||
right: 12,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
maxHeight: "75%",
|
||||
alignSelf: "center",
|
||||
},
|
||||
dotsWrapperNative: {
|
||||
marginLeft: 12,
|
||||
alignSelf: "center",
|
||||
},
|
||||
dotsContainer: {
|
||||
flexDirection: "column",
|
||||
},
|
||||
dot: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
marginHorizontal: 0,
|
||||
marginVertical: 6,
|
||||
borderRadius: 999,
|
||||
backgroundColor: "rgba(255,255,255,0.4)",
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user