Skip to content

Commit

Permalink
Change .web files to default and add .native (#2835)
Browse files Browse the repository at this point in the history
## Description

This PR moves content of `.web` files into default ones, which are replaced by `.native`. We do this because `metro` is good at resolving those types of files, but other bundlers are not.

Change in `tsconfig` assures, that types are correctly resolved.

## Test plan

Built and played a little bit with example app on:

- Web
- iOS
- Android
  • Loading branch information
m-bert authored Apr 2, 2024
1 parent abafec7 commit e3e0b79
Show file tree
Hide file tree
Showing 28 changed files with 389 additions and 382 deletions.
5 changes: 5 additions & 0 deletions src/RNGestureHandlerModule.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Reexport the native module spec used by codegen. The relevant files are inluded on Android
// to ensure the compatibility with the old arch, while iOS doesn't require those at all.

import Module from './specs/NativeRNGestureHandlerModule';
export default Module;
108 changes: 104 additions & 4 deletions src/RNGestureHandlerModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,105 @@
// Reexport the native module spec used by codegen. The relevant files are inluded on Android
// to ensure the compatibility with the old arch, while iOS doesn't require those at all.
import React from 'react';

import Module from './specs/NativeRNGestureHandlerModule';
export default Module;
import type { ActionType } from './ActionType';
import { isNewWebImplementationEnabled } from './EnableNewWebImplementation';
import { Gestures, HammerGestures } from './web/Gestures';
import type { Config } from './web/interfaces';
import InteractionManager from './web/tools/InteractionManager';
import NodeManager from './web/tools/NodeManager';
import * as HammerNodeManager from './web_hammer/NodeManager';
import { GestureHandlerWebDelegate } from './web/tools/GestureHandlerWebDelegate';

export default {
handleSetJSResponder(tag: number, blockNativeResponder: boolean) {
console.warn('handleSetJSResponder: ', tag, blockNativeResponder);
},
handleClearJSResponder() {
console.warn('handleClearJSResponder: ');
},
createGestureHandler<T>(
handlerName: keyof typeof Gestures,
handlerTag: number,
config: T
) {
if (isNewWebImplementationEnabled()) {
if (!(handlerName in Gestures)) {
throw new Error(
`react-native-gesture-handler: ${handlerName} is not supported on web.`
);
}

const GestureClass = Gestures[handlerName];
NodeManager.createGestureHandler(
handlerTag,
new GestureClass(new GestureHandlerWebDelegate())
);
InteractionManager.getInstance().configureInteractions(
NodeManager.getHandler(handlerTag),
config as unknown as Config
);
} else {
if (!(handlerName in HammerGestures)) {
throw new Error(
`react-native-gesture-handler: ${handlerName} is not supported on web.`
);
}

// @ts-ignore If it doesn't exist, the error is thrown
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const GestureClass = HammerGestures[handlerName];
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
HammerNodeManager.createGestureHandler(handlerTag, new GestureClass());
}

this.updateGestureHandler(handlerTag, config as unknown as Config);
},
attachGestureHandler(
handlerTag: number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
newView: any,
_actionType: ActionType,
propsRef: React.RefObject<unknown>
) {
if (
!(newView instanceof HTMLElement || newView instanceof React.Component)
) {
return;
}

if (isNewWebImplementationEnabled()) {
//@ts-ignore Types should be HTMLElement or React.Component
NodeManager.getHandler(handlerTag).init(newView, propsRef);

Check warning on line 71 in src/RNGestureHandlerModule.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe call of an `any` typed value
} else {
//@ts-ignore Types should be HTMLElement or React.Component
HammerNodeManager.getHandler(handlerTag).setView(newView, propsRef);
}
},
updateGestureHandler(handlerTag: number, newConfig: Config) {
if (isNewWebImplementationEnabled()) {
NodeManager.getHandler(handlerTag).updateGestureConfig(newConfig);

InteractionManager.getInstance().configureInteractions(
NodeManager.getHandler(handlerTag),
newConfig
);
} else {
HammerNodeManager.getHandler(handlerTag).updateGestureConfig(newConfig);
}
},
getGestureHandlerNode(handlerTag: number) {
if (isNewWebImplementationEnabled()) {
return NodeManager.getHandler(handlerTag);
} else {
return HammerNodeManager.getHandler(handlerTag);
}
},
dropGestureHandler(handlerTag: number) {
if (isNewWebImplementationEnabled()) {
NodeManager.dropGestureHandler(handlerTag);
} else {
HammerNodeManager.dropGestureHandler(handlerTag);
}
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
flushOperations() {},
};
105 changes: 0 additions & 105 deletions src/RNGestureHandlerModule.web.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/RNRenderer.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
export { default as RNRenderer } from 'react-native/Libraries/Renderer/shims/ReactNative';
6 changes: 3 additions & 3 deletions src/RNRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
export { default as RNRenderer } from 'react-native/Libraries/Renderer/shims/ReactNative';
export const RNRenderer = {
findHostInstance_DEPRECATED: (_ref: any) => null,

Check warning on line 2 in src/RNRenderer.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
};
3 changes: 0 additions & 3 deletions src/RNRenderer.web.ts

This file was deleted.

148 changes: 148 additions & 0 deletions src/components/GestureComponents.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import * as React from 'react';
import {
PropsWithChildren,
ForwardedRef,
RefAttributes,
ReactElement,
} from 'react';
import {
ScrollView as RNScrollView,
ScrollViewProps as RNScrollViewProps,
Switch as RNSwitch,
SwitchProps as RNSwitchProps,
TextInput as RNTextInput,
TextInputProps as RNTextInputProps,
DrawerLayoutAndroid as RNDrawerLayoutAndroid,
DrawerLayoutAndroidProps as RNDrawerLayoutAndroidProps,
FlatList as RNFlatList,
FlatListProps as RNFlatListProps,
RefreshControl as RNRefreshControl,
} from 'react-native';

import createNativeWrapper from '../handlers/createNativeWrapper';

import {
NativeViewGestureHandlerProps,
nativeViewProps,
} from '../handlers/NativeViewGestureHandler';

import { toArray } from '../utils';

export const RefreshControl = createNativeWrapper(RNRefreshControl, {
disallowInterruption: true,
shouldCancelWhenOutside: false,
});
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type RefreshControl = typeof RefreshControl & RNRefreshControl;

const GHScrollView = createNativeWrapper<PropsWithChildren<RNScrollViewProps>>(
RNScrollView,
{
disallowInterruption: true,
shouldCancelWhenOutside: false,
}
);
export const ScrollView = React.forwardRef<
RNScrollView,
RNScrollViewProps & NativeViewGestureHandlerProps
>((props, ref) => {
const refreshControlGestureRef = React.useRef<RefreshControl>(null);
const { refreshControl, waitFor, ...rest } = props;

return (
<GHScrollView
{...rest}
// @ts-ignore `ref` exists on `GHScrollView`
ref={ref}
waitFor={[...toArray(waitFor ?? []), refreshControlGestureRef]}
// @ts-ignore we don't pass `refreshing` prop as we only want to override the ref
refreshControl={
refreshControl
? React.cloneElement(refreshControl, {
// @ts-ignore for reasons unknown to me, `ref` doesn't exist on the type inferred by TS
ref: refreshControlGestureRef,
})
: undefined
}
/>
);
});
// backward type compatibility with https://github.com/software-mansion/react-native-gesture-handler/blob/db78d3ca7d48e8ba57482d3fe9b0a15aa79d9932/react-native-gesture-handler.d.ts#L440-L457
// include methods of wrapped components by creating an intersection type with the RN component instead of duplicating them.
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type ScrollView = typeof GHScrollView & RNScrollView;

export const Switch = createNativeWrapper<RNSwitchProps>(RNSwitch, {
shouldCancelWhenOutside: false,
shouldActivateOnStart: true,
disallowInterruption: true,
});
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type Switch = typeof Switch & RNSwitch;

export const TextInput = createNativeWrapper<RNTextInputProps>(RNTextInput);
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type TextInput = typeof TextInput & RNTextInput;

export const DrawerLayoutAndroid = createNativeWrapper<
PropsWithChildren<RNDrawerLayoutAndroidProps>
>(RNDrawerLayoutAndroid, { disallowInterruption: true });
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type DrawerLayoutAndroid = typeof DrawerLayoutAndroid &
RNDrawerLayoutAndroid;

export const FlatList = React.forwardRef((props, ref) => {
const refreshControlGestureRef = React.useRef<RefreshControl>(null);

const { waitFor, refreshControl, ...rest } = props;

const flatListProps = {};
const scrollViewProps = {};
for (const [propName, value] of Object.entries(rest)) {
// https://github.com/microsoft/TypeScript/issues/26255
if ((nativeViewProps as readonly string[]).includes(propName)) {
// @ts-ignore - this function cannot have generic type so we have to ignore this error
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
scrollViewProps[propName] = value;
} else {
// @ts-ignore - this function cannot have generic type so we have to ignore this error
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
flatListProps[propName] = value;
}
}

return (
// @ts-ignore - this function cannot have generic type so we have to ignore this error
<RNFlatList
ref={ref}
{...flatListProps}
renderScrollComponent={(scrollProps) => (
<ScrollView
{...{
...scrollProps,
...scrollViewProps,
waitFor: [...toArray(waitFor ?? []), refreshControlGestureRef],
}}
/>
)}
// @ts-ignore we don't pass `refreshing` prop as we only want to override the ref
refreshControl={
refreshControl
? React.cloneElement(refreshControl, {
// @ts-ignore for reasons unknown to me, `ref` doesn't exist on the type inferred by TS
ref: refreshControlGestureRef,
})
: undefined
}
/>
);
}) as <ItemT = any>(
props: PropsWithChildren<
RNFlatListProps<ItemT> &
RefAttributes<FlatList<ItemT>> &
NativeViewGestureHandlerProps
>,
ref: ForwardedRef<FlatList<ItemT>>
) => ReactElement | null;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type FlatList<ItemT = any> = typeof FlatList & RNFlatList<ItemT>;
Loading

0 comments on commit e3e0b79

Please sign in to comment.