Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3cb1164
add missing types
akwasniewski Oct 3, 2025
a26c22d
Merge branch 'next' into @akwasniewski/add-missing-types
akwasniewski Oct 3, 2025
052bfb7
addded type suffix
akwasniewski Oct 3, 2025
652fe3f
exporting single and composed gesture types
akwasniewski Oct 3, 2025
43bd77d
SingleGestureType as a union
akwasniewski Oct 3, 2025
2b291b6
new api gets better names
akwasniewski Oct 3, 2025
704d604
import, export type
akwasniewski Oct 6, 2025
4b3451d
update types in tests and examples
akwasniewski Oct 6, 2025
212bbc2
Merge branch 'next' into @akwasniewski/add-missing-types
akwasniewski Oct 6, 2025
679ab46
export correct types
akwasniewski Oct 6, 2025
a5c00bc
first examples
akwasniewski Sep 29, 2025
2d8d8f0
calculator, hover, overlap
akwasniewski Oct 1, 2025
44273d7
velocity test
akwasniewski Oct 1, 2025
f92baae
fix wrong import
akwasniewski Oct 6, 2025
997790c
Merge branch '@akwasniewski/add-missing-types' into @akwasniewski/v3-…
akwasniewski Oct 6, 2025
762c71c
more specific export
akwasniewski Oct 6, 2025
514a81d
Merge branch '@akwasniewski/add-missing-types' into @akwasniewski/v3-…
akwasniewski Oct 6, 2025
fb3be0e
even more specific type
akwasniewski Oct 7, 2025
2126afb
Merge branch '@akwasniewski/add-missing-types' into @akwasniewski/v3-…
akwasniewski Oct 7, 2025
b2524ae
bottom sheet
akwasniewski Oct 7, 2025
1e52497
chat heads
akwasniewski Oct 7, 2025
4351897
hoverable icons
akwasniewski Oct 7, 2025
f30c850
camera
akwasniewski Oct 7, 2025
ede4215
fix app
akwasniewski Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions apps/common-app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ import LongPressExample from './src/simple/longPress';
import ManualExample from './src/simple/manual';
import SimpleFling from './src/simple/fling';

import Lock from './src/v3_api/lock/lock';
import V3Fling from './src/v3_api/fling/fling';
import LogicDetectorExample from './src/v3_api/svg/svg';
import V3Hover from './src/v3_api/hover/index';
import V3Overlap from './src/v3_api/overlap/index';
import V3Calculator from './src/v3_api/calculator/index';
import V3Velocity from './src/v3_api/velocity_test/index';
import V3BottomSheet from './src/v3_api/bottom_sheet/index';
import V3ChatHeads from './src/v3_api/chat_heads/index';
import V3HoverableIcons from './src/v3_api/hoverable_icons/index';
import V3Camera from './src/v3_api/camera/index';
import { Icon } from '@swmansion/icons';

interface Example {
Expand All @@ -98,6 +109,22 @@ const EXAMPLES: ExamplesSection[] = [
sectionTitle: 'Empty',
data: [{ name: 'Empty Example', component: EmptyExample }],
},
{
sectionTitle: 'V3 api',
data: [
{ name: 'V3 Fling', component: V3Fling },
{ name: 'Svg', component: LogicDetectorExample },
{ name: 'Lock', component: Lock },
{ name: 'V3 Hover', component: V3Hover },
{ name: 'V3 Overlap', component: V3Overlap },
{ name: 'V3 Calculator', component: V3Calculator },
{ name: 'V3 Velocity Test', component: V3Velocity },
{ name: 'V3 Bottom Sheet', component: V3BottomSheet },
{ name: 'V3 Chat Heads', component: V3ChatHeads },
{ name: 'V3 Hoverable Icons', component: V3HoverableIcons },
{ name: 'V3 Camera', component: V3Camera },
],
},
{
sectionTitle: 'New api',
data: [
Expand Down
8 changes: 4 additions & 4 deletions apps/common-app/src/new_api/drag_n_drop/Draggable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
PanGestureHandlerEventPayload,
Gesture,
GestureDetector,
PanGesture,
TapGesture,
PanGestureType,
TapGestureType,
} from 'react-native-gesture-handler';
import Animated, { runOnJS, useAnimatedStyle } from 'react-native-reanimated';

Expand All @@ -22,8 +22,8 @@ interface DraggableProps {
isActive: boolean;
translation: AnimatedPostion;
position: { x: number; y: number };
dragGesture: PanGesture;
tapEndGesture: TapGesture;
dragGesture: PanGestureType;
tapEndGesture: TapGestureType;
tileSize: number;
rowGap: number;
columnGap: number;
Expand Down
162 changes: 162 additions & 0 deletions apps/common-app/src/v3_api/bottom_sheet/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import React, { useState } from 'react';
import {
Dimensions,
NativeScrollEvent,
NativeSyntheticEvent,
StyleSheet,
View,
} from 'react-native';
import {
NativeDetector,
PanGestureEvent,
useNative,
usePan,
useSimultaneous,
useTap,
} from 'react-native-gesture-handler';
import Animated, {
runOnJS,
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
import { LoremIpsum } from '../../common';

const HEADER_HEIGTH = 50;
const windowHeight = Dimensions.get('window').height;
const SNAP_POINTS_FROM_TOP = [50, windowHeight * 0.4, windowHeight * 0.8];

const FULLY_OPEN_SNAP_POINT = SNAP_POINTS_FROM_TOP[0];
const CLOSED_SNAP_POINT = SNAP_POINTS_FROM_TOP[SNAP_POINTS_FROM_TOP.length - 1];

function Example() {
const [snapPoint, setSnapPoint] = useState(CLOSED_SNAP_POINT);
const translationY = useSharedValue(0);
const scrollOffset = useSharedValue(0);
const bottomSheetTranslateY = useSharedValue(CLOSED_SNAP_POINT);

const onHandlerEndOnJS = (point: number) => {
setSnapPoint(point);
};
const onHandlerEnd = (e: PanGestureEvent) => {
'worklet';
const dragToss = 0.01;
const endOffsetY =
bottomSheetTranslateY.value +
translationY.value +
e.handlerData.velocityY * dragToss;

// calculate nearest snap point
let destSnapPoint = FULLY_OPEN_SNAP_POINT;

if (
snapPoint === FULLY_OPEN_SNAP_POINT &&
endOffsetY < FULLY_OPEN_SNAP_POINT
) {
return;
}
for (const snapPoint of SNAP_POINTS_FROM_TOP) {
const distFromSnap = Math.abs(snapPoint - endOffsetY);
if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
destSnapPoint = snapPoint;
}
}

// update current translation to be able to animate withSpring to snapPoint
bottomSheetTranslateY.value =
bottomSheetTranslateY.value + translationY.value;
translationY.value = 0;

bottomSheetTranslateY.value = withSpring(destSnapPoint, {
mass: 0.5,
});
runOnJS(onHandlerEndOnJS)(destSnapPoint);
};
const panGesture = usePan({
onUpdate: (e) => {
'worklet';
// when bottom sheet is not fully opened scroll offset should not influence
// its position (prevents random snapping when opening bottom sheet when
// the content is already scrolled)
if (snapPoint === FULLY_OPEN_SNAP_POINT) {
translationY.value = e.handlerData.translationY - scrollOffset.value;
} else {
translationY.value = e.handlerData.translationY;
}
},
onEnd: onHandlerEnd,
});

const blockScrollUntilAtTheTop = useTap({
maxDeltaY: snapPoint - FULLY_OPEN_SNAP_POINT,
maxDuration: 100000,
simultaneousWithExternalGesture: panGesture,
});

const headerGesture = usePan({
onUpdate: (e) => {
'worklet';
translationY.value = e.handlerData.translationY;
},
onEnd: onHandlerEnd,
});

const scrollViewGesture = useNative({
requireExternalGestureToFail: blockScrollUntilAtTheTop,
});

const bottomSheetAnimatedStyle = useAnimatedStyle(() => {
const translateY = bottomSheetTranslateY.value + translationY.value;

const minTranslateY = Math.max(FULLY_OPEN_SNAP_POINT, translateY);
const clampedTranslateY = Math.min(CLOSED_SNAP_POINT, minTranslateY);
return {
transform: [{ translateY: clampedTranslateY }],
};
});

const simultanousGesture = useSimultaneous(panGesture, scrollViewGesture);

return (
<View style={styles.container}>
<LoremIpsum words={200} />
<NativeDetector gesture={blockScrollUntilAtTheTop}>
<Animated.View style={[styles.bottomSheet, bottomSheetAnimatedStyle]}>
<NativeDetector gesture={headerGesture}>
<View style={styles.header} />
</NativeDetector>
<NativeDetector gesture={simultanousGesture}>
<Animated.ScrollView
bounces={false}
scrollEventThrottle={1}
onScrollBeginDrag={(
e: NativeSyntheticEvent<NativeScrollEvent>
) => {
scrollOffset.value = e.nativeEvent.contentOffset.y;
}}>
<LoremIpsum />
<LoremIpsum />
<LoremIpsum />
</Animated.ScrollView>
</NativeDetector>
</Animated.View>
</NativeDetector>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
height: HEADER_HEIGTH,
backgroundColor: 'coral',
},
bottomSheet: {
...StyleSheet.absoluteFillObject,
backgroundColor: '#ff9f7A',
},
});

export default Example;
Loading
Loading