Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ PODS:
- React-RCTText
- ReactCommon/turbomodule/core
- Yoga
- RNSVG (12.2.0):
- RNSVG (12.3.0):
- React-Core
- SocketRocket (0.6.0)
- Yoga (1.14.0)
Expand Down Expand Up @@ -577,7 +577,7 @@ SPEC CHECKSUMS:
ReactCommon: bf2888a826ceedf54b99ad1b6182d1bc4a8a3984
RNGestureHandler: 50e6ffee79932d14ea747d4ea4cc99aac0f24e86
RNReanimated: e42de406edd11350af29016cf6802ef16ee364d0
RNSVG: 4ecc2e8f38b6ebe7889909570c26f3abe8059767
RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: 17cd9a50243093b547c1e539c749928dd68152da
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
Expand Down
133 changes: 61 additions & 72 deletions src/components/Examples/ScrollViewKeyboardAvoidExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Animated, {
} from 'react-native-reanimated';
import styled from 'styled-components/native';
import InputField from '../InputField';
import Regular from '../InputField/Regular';
import InputAnimationWrapper from '../InputField/InputAnimationWrapper';
import ScrollViewKeyboardAvoid from '../ScrollViewKeyboardAvoid';
import { SCROLL_EVENT_THROTTLE } from '../../constants/configs';

Expand All @@ -23,39 +25,22 @@ const inputStyle = {
backgroundColor: 'white',
};

const fakeScrollItem = [
{
text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
const text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
},
{
text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
},
{
text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
},
];
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
`;

const Wrapper = styled.View<{ windowHeight: number }>`
height: ${({ windowHeight }): number => windowHeight}px;
width: 100%;
`;

const FakeContentWrapper = styled.View<{ windowHeight: number }>`
background: white;
height: ${({ windowHeight }): number => windowHeight}px;
const FakeContentWrapper = styled.View`
height: 100%;
width: 100%;
border: 2px solid black;
padding: 32px 16px;
`;

Expand All @@ -66,12 +51,27 @@ const ScrollViewKeyboardAvoidExample: React.FC = () => {
const scrollViewRef = useAnimatedRef<Animated.ScrollView>();
const windowHeight = useWindowDimensions().height;
const translationY = useSharedValue(0);

const scrollY = useSharedValue(0);
const scrollViewHeight = useSharedValue(0);
const contentHeight = useSharedValue(0);
const keyboardHeight = useSharedValue(0);
const isInputFieldFocused = useSharedValue(false);
const isScrollable = useSharedValue(false);
const isKeyboardVisible = useSharedValue(false);

const animationScrollValues = {
scrollY,
scrollViewHeight,
contentHeight,
keyboardHeight,
isInputFieldFocused,
isScrollable,
isKeyboardVisible,
};

const animatedStyle = useAnimatedStyle(() => {
return {
position: 'absolute',
bottom: 0,
height: isKeyboardVisible?.value ? 150 : windowHeight,
transform: [
{
translateY: translationY.value,
Expand All @@ -82,52 +82,41 @@ const ScrollViewKeyboardAvoidExample: React.FC = () => {

return (
<Wrapper windowHeight={windowHeight}>
<AnimatedWrapper style={animatedStyle}>
<ScrollViewKeyboardAvoid
ref={scrollViewRef}
bounces={false}
alwaysBounceVertical={false}
translationYValues={[translationY]}
keyboardAvoidBottomMargin={isIOS ? 64 : 100}
connectScrollViewMeasuresToAnimationValues={{
isKeyboardVisible,
}}
fadingScrollEdges={{
isEnabled: true,
iOSandWebFadingEdgeHeight: 150,
nativeBackgroundColor: 'black',
webBackgroundColorTop: {
from: 'rgba(0, 0, 0, 0.05)',
to: 'rgba(0,0,0,0.05)',
},
webBackgroundColorBottom: {
to: 'rgba(0, 0, 0, 0.05)',
from: 'rgba(0,0,0,0.05)',
},
}}
scrollArrows={{
isEnabled: true,
dimensions: 40,
fill: 'black',
topArrowOffset: 40,
bottomArrowOffset: 40,
}}
scrollEventThrottle={SCROLL_EVENT_THROTTLE}
>
<>
{fakeScrollItem.map(({ text }, i) => (
<Fragment key={i}>
<FakeContentWrapper windowHeight={windowHeight} key={`${i}_${text}`}>
<Text>
{text} <InputField uniqueId="wtf" style={inputStyle} />
</Text>
</FakeContentWrapper>
<InputField uniqueId={i} style={inputStyle} />
</Fragment>
))}
</>
</ScrollViewKeyboardAvoid>
</AnimatedWrapper>
<ScrollViewKeyboardAvoid
ref={scrollViewRef}
bounces={false}
alwaysBounceVertical={false}
keyboardAvoidBottomMargin={isIOS ? 64 : 100}
connectScrollViewMeasuresToAnimationValues={animationScrollValues}
fadingScrollEdges={{
isEnabled: true,
iOSandWebFadingEdgeHeight: 150,
nativeBackgroundColor: 'black',
webBackgroundColorTop: {
from: 'rgba(0, 0, 0, 0.05)',
to: 'rgba(0,0,0,0.05)',
},
webBackgroundColorBottom: {
to: 'rgba(0, 0, 0, 0.05)',
from: 'rgba(0,0,0,0.05)',
},
}}
scrollArrows={{
isEnabled: true,
dimensions: 40,
fill: 'black',
topArrowOffset: 40,
bottomArrowOffset: 40,
}}
scrollEventThrottle={SCROLL_EVENT_THROTTLE}
>
<FakeContentWrapper>
<Text>{text}</Text>
<InputAnimationWrapper {...animationScrollValues}>
<Regular placeholder="Testing inputfield" />
</InputAnimationWrapper>
</FakeContentWrapper>
</ScrollViewKeyboardAvoid>
</Wrapper>
);
};
Expand Down
98 changes: 98 additions & 0 deletions src/components/InputField/InputAnimationWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useEffect } from 'react';
import { useWindowDimensions } from 'react-native';
import Animated, {
useAnimatedReaction,
useAnimatedRef,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';

import { getAnimatedMeasures } from '../../helpers';

interface InputAnimationWrapperProps {
scrollY: Animated.SharedValue<number>;
scrollViewHeight: Animated.SharedValue<number>;
contentHeight: Animated.SharedValue<number>;
keyboardHeight: Animated.SharedValue<number>;
isKeyboardVisible: Animated.SharedValue<boolean>;
isInputFieldFocused: Animated.SharedValue<boolean>;
isScrollable: Animated.SharedValue<boolean>;
}

const InputAnimationWrapper: React.FC<InputAnimationWrapperProps> = ({
scrollY,
scrollViewHeight,
contentHeight,
keyboardHeight,
isKeyboardVisible,
isInputFieldFocused,
isScrollable,
children,
}) => {
const animatedRef = useAnimatedRef<Animated.View>();
const positionY = useSharedValue(0);
const translationY = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
position: 'absolute',
zIndex: 999,
height: windowHeight,
backgroundColor: 'transparent',
bottom: translationY.value,
}));

const { height: windowHeight } = useWindowDimensions();

/* TODO:
- Ensure that translation Y is right above keyboard.
The value is probably coming from the outside
- Add flexibility to input position

*/

useEffect(() => {
if (animatedRef?.current) {
getAnimatedMeasures({
ref: animatedRef,
callback: ({ y }) => {
if (positionY.value !== y) {
positionY.value = y;
}
},
});
}
});
useAnimatedReaction(
() => isInputFieldFocused,
(curr, prev) => {
if (curr.value) {
const keyboardYPosition = windowHeight - keyboardHeight.value;
const diff = positionY.value - keyboardYPosition;

// console.log('InputfField is focused', {
// keyboardHeight: keyboardHeight.value,
// contentHeight: contentHeight.value,
// positionY: positionY.value,
// keyboardYPosition,
// windowHeight,
// diff,
// });

translationY.value = withTiming(-50, { duration: 250 }, isAnimationDone => {
if (isAnimationDone) {
console.log('animation is done');
}
});
}
},
[isInputFieldFocused],
);

return (
<Animated.View ref={animatedRef} style={animatedStyle}>
{children}
</Animated.View>
);
};

export default InputAnimationWrapper;
38 changes: 38 additions & 0 deletions src/components/InputField/Regular.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { TextInput as RNTextInput } from 'react-native';
import styled from 'styled-components/native';

interface InputFieldProps {
placeholder: string;
}

const AVAILABLE_SPACE_DESKTOP = 68;
const AVAILABLE_SPACE_MOBILE = 38;

const TextInput = styled.TextInput``;

const Regular: React.FC<InputFieldProps> = ({ placeholder }) => {
const TEXT_INPUT_STYLE = {
padding: 6,
fontSize: 40,
fontWeight: 'bold',
color: 'black',
textAlign: 'center',
outline: 'none',
borderWidth: 3,
borderColor: 'grey',
borderRadius: 7,
textShadow: '2px 2px 5px rgba(0, 0, 0, 0.85)',
};

return (
<TextInput
style={TEXT_INPUT_STYLE as any}
placeholder={placeholder}
placeholderTextColor="grey"
keyboardType="default"
/>
);
};

export default Regular;
3 changes: 2 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import ScrollViewKeyboardAvoidExample from './components/Examples/ScrollViewKeyboardAvoidExample';
import NoHardRerenderingEffect from './components/Examples/NoHardRerenderingEffect';
import 'setimmediate';

const App: React.FC = () => <NoHardRerenderingEffect />;
const App: React.FC = () => <ScrollViewKeyboardAvoidExample />;

export default App;