From 60db050c5249b0d1c54a6fbabc14664923b6f0ed Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 27 Jul 2024 13:37:05 +0200 Subject: [PATCH 1/5] Move react-native-web styles to a separate package --- package.json | 1 + src/components/Composer/index.tsx | 8 +- src/components/SafeAreaConsumer/types.ts | 8 +- src/components/ScreenWrapper.tsx | 4 +- .../getRootNavigatorScreenOptions.ts | 4 +- src/styles/utils/display.ts | 12 +- .../ReportActionContextMenuStyleUtils.ts | 3 +- src/styles/utils/index.ts | 59 +-- src/styles/utils/overflowAuto/index.ts | 4 +- src/styles/utils/positioning.ts | 3 +- src/types/modules/react-native.d.ts | 365 +----------------- 11 files changed, 53 insertions(+), 418 deletions(-) diff --git a/package.json b/package.json index 5da6a291f261..6ec59d2e1fd3 100644 --- a/package.json +++ b/package.json @@ -238,6 +238,7 @@ "@types/node": "^20.11.5", "@types/pusher-js": "^5.1.0", "@types/react": "18.2.45", + "@types/react-native-web": "0.73.0", "@types/react-beautiful-dnd": "^13.1.4", "@types/react-collapse": "^5.0.1", "@types/react-dom": "^18.2.4", diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 3889c8597843..1df90950b9e7 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -4,7 +4,7 @@ import type {BaseSyntheticEvent, ForwardedRef} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {flushSync} from 'react-dom'; // eslint-disable-next-line no-restricted-imports -import type {DimensionValue, NativeSyntheticEvent, Text as RNText, TextInput, TextInputKeyPressEventData, TextInputSelectionChangeEventData, TextStyle} from 'react-native'; +import type {NativeSyntheticEvent, Text as RNText, TextInput, TextInputKeyPressEventData, TextInputSelectionChangeEventData, TextStyle, ViewStyle} from 'react-native'; import {DeviceEventEmitter, StyleSheet, View} from 'react-native'; import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput'; import RNMarkdownTextInput from '@components/RNMarkdownTextInput'; @@ -101,7 +101,7 @@ function Composer( }); const [caretContent, setCaretContent] = useState(''); const [valueBeforeCaret, setValueBeforeCaret] = useState(''); - const [textInputWidth, setTextInputWidth] = useState(''); + const [textInputWidth, setTextInputWidth] = useState(''); const [isRendered, setIsRendered] = useState(false); const isScrollBarVisible = useIsScrollBarVisible(textInput, value ?? ''); const [prevScroll, setPrevScroll] = useState(); @@ -318,7 +318,7 @@ function Composer( opacity: 0, }} > - + {`${valueBeforeCaret} `} ; diff --git a/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts index b3f3a816d09a..1894f95a44c1 100644 --- a/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts +++ b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts @@ -20,8 +20,7 @@ const getMiniWrapperStyle = (theme: ThemeColors, styles: ThemeStyles): ViewStyle borderWidth: 1, borderColor: theme.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - // NOTE: asserting "transform" to a valid type, because it isn't possible to augment "transform". - transform: 'translateZ(0)' as unknown as ViewStyle['transform'], + transform: 'translateZ(0)', }, ]; diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index ed76abd484d0..56dd9a583c9a 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1,5 +1,5 @@ import {StyleSheet} from 'react-native'; -import type {AnimatableNumericValue, Animated, ColorValue, DimensionValue, ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native'; +import type {AnimatableNumericValue, Animated, ColorValue, ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; import type {ValueOf} from 'type-fest'; @@ -342,7 +342,6 @@ function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle { return {marginBottom: (insets?.bottom ?? 0) * variables.safeInsertPercentage}; } -// NOTE: asserting some web style properties to a valid type, because it isn't possible to augment them. function getZoomSizingStyle( isZoomed: boolean, imgWidth: number, @@ -356,23 +355,23 @@ function getZoomSizingStyle( if (isLoading || imgWidth === 0 || imgHeight === 0) { return undefined; } - const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as DimensionValue; - const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as DimensionValue; + const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px`; + const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px`; // Return different size and offset style based on zoomScale and isZoom. if (isZoomed) { // When both width and height are smaller than container(modal) size, set the height by multiplying zoomScale if it is zoomed in. if (zoomScale >= 1) { return { - height: `${imgHeight * zoomScale}px` as DimensionValue, - width: `${imgWidth * zoomScale}px` as DimensionValue, + height: `${imgHeight * zoomScale}px`, + width: `${imgWidth * zoomScale}px`, }; } // If image height and width are bigger than container size, display image with original size because original size is bigger and position absolute. return { - height: `${imgHeight}px` as DimensionValue, - width: `${imgWidth}px` as DimensionValue, + height: `${imgHeight}px`, + width: `${imgWidth}px`, top, left, }; @@ -381,8 +380,8 @@ function getZoomSizingStyle( // If image is not zoomed in and image size is smaller than container size, display with original size based on offset and position absolute. if (zoomScale > 1) { return { - height: `${imgHeight}px` as DimensionValue, - width: `${imgWidth}px` as DimensionValue, + height: `${imgHeight}px`, + width: `${imgWidth}px`, top, left, }; @@ -390,11 +389,11 @@ function getZoomSizingStyle( // If image is bigger than container size, display full image in the screen with scaled size (fit by container size) and position absolute. // top, left offset should be different when displaying long or wide image. - const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as DimensionValue; - const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as DimensionValue; + const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px`; + const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px`; return { - height: `${imgHeight * zoomScale}px` as DimensionValue, - width: `${imgWidth * zoomScale}px` as DimensionValue, + height: `${imgHeight * zoomScale}px`, + width: `${imgWidth * zoomScale}px`, top: scaledTop, left: scaledLeft, }; @@ -538,6 +537,8 @@ function getButtonStyleWithIcon( } } +type MarginPaddingValue = ViewStyle['marginTop' | 'marginBottom' | 'paddingTop' | 'paddingBottom']; + /** * Combine margin/padding with safe area inset * @@ -545,10 +546,14 @@ function getButtonStyleWithIcon( * @param safeAreaValue - safe area inset * @param shouldAddSafeAreaValue - indicator whether safe area inset should be applied */ -function getCombinedSpacing(modalContainerValue: DimensionValue | undefined, safeAreaValue: number, shouldAddSafeAreaValue: boolean): number | DimensionValue | undefined { +function getCombinedSpacing(modalContainerValue: MarginPaddingValue, safeAreaValue: number, shouldAddSafeAreaValue: boolean): MarginPaddingValue { // modalContainerValue can only be added to safe area inset if it's a number, otherwise it's returned as is - if (typeof modalContainerValue === 'number' || !modalContainerValue) { - return (modalContainerValue ?? 0) + (shouldAddSafeAreaValue ? safeAreaValue : 0); + if (typeof modalContainerValue === 'number') { + return modalContainerValue + (shouldAddSafeAreaValue ? safeAreaValue : 0); + } + + if (!modalContainerValue) { + return shouldAddSafeAreaValue ? safeAreaValue : 0; } return modalContainerValue; @@ -563,10 +568,10 @@ type ModalPaddingStylesParams = { safeAreaPaddingBottom: number; safeAreaPaddingLeft: number; safeAreaPaddingRight: number; - modalContainerStyleMarginTop: DimensionValue | undefined; - modalContainerStyleMarginBottom: DimensionValue | undefined; - modalContainerStylePaddingTop: DimensionValue | undefined; - modalContainerStylePaddingBottom: DimensionValue | undefined; + modalContainerStyleMarginTop: MarginPaddingValue; + modalContainerStyleMarginBottom: MarginPaddingValue; + modalContainerStylePaddingTop: MarginPaddingValue; + modalContainerStylePaddingBottom: MarginPaddingValue; insets: EdgeInsets; }; @@ -964,8 +969,7 @@ function getCheckboxPressableStyle(borderRadius = 6): ViewStyle { return { justifyContent: 'center', alignItems: 'center', - // eslint-disable-next-line object-shorthand - borderRadius: borderRadius, + borderRadius, }; } @@ -1311,8 +1315,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ borderWidth: 2, justifyContent: 'center', alignItems: 'center', - // eslint-disable-next-line object-shorthand - borderRadius: borderRadius, + borderRadius, }), /** @@ -1338,13 +1341,13 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ /** * Get the style for the AM and PM buttons in the TimePicker */ - getStatusAMandPMButtonStyle: (amPmValue: string): {styleForAM: ViewStyle; styleForPM: ViewStyle} => { + getStatusAMandPMButtonStyle: (amPmValue: string): {styleForAM: StyleProp; styleForPM: StyleProp} => { const computedStyleForAM: ViewStyle = amPmValue !== CONST.TIME_PERIOD.AM ? {backgroundColor: theme.componentBG} : {}; const computedStyleForPM: ViewStyle = amPmValue !== CONST.TIME_PERIOD.PM ? {backgroundColor: theme.componentBG} : {}; return { - styleForAM: [styles.timePickerWidth100, computedStyleForAM] as unknown as ViewStyle, - styleForPM: [styles.timePickerWidth100, computedStyleForPM] as unknown as ViewStyle, + styleForAM: [styles.timePickerWidth100, computedStyleForAM], + styleForPM: [styles.timePickerWidth100, computedStyleForPM], }; }, diff --git a/src/styles/utils/overflowAuto/index.ts b/src/styles/utils/overflowAuto/index.ts index 670a0b9643eb..db55b65b8949 100644 --- a/src/styles/utils/overflowAuto/index.ts +++ b/src/styles/utils/overflowAuto/index.ts @@ -1,12 +1,10 @@ -import type {ViewStyle} from 'react-native'; import type OverflowAutoStyles from './types'; /** * Web-only style. */ const overflowAuto: OverflowAutoStyles = { - // NOTE: asserting "overflow" to a valid type, because it isn't possible to augment "overflow". - overflow: 'auto' as ViewStyle['overflow'], + overflow: 'auto', }; export default overflowAuto; diff --git a/src/styles/utils/positioning.ts b/src/styles/utils/positioning.ts index c266a7469bc2..71cee84b18db 100644 --- a/src/styles/utils/positioning.ts +++ b/src/styles/utils/positioning.ts @@ -15,8 +15,7 @@ export default { * Web-only style. */ pFixed: { - // NOTE: asserting "position" to a valid type, because it isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + position: 'fixed', }, t0: { diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 87628fded0cc..508c89a6e561 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,12 +1,6 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -/* eslint-disable @typescript-eslint/no-empty-interface */ - /* eslint-disable @typescript-eslint/consistent-type-definitions */ -// eslint-disable-next-line no-restricted-imports -import type {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler, TouchEventHandler, UIEventHandler, WheelEventHandler} from 'react'; -import 'react-native'; -import type {GestureResponderEvent, LayoutRectangle, NativeSyntheticEvent, TargetedEvent} from 'react-native'; +import '@types/react-native-web'; +import type {TargetedEvent} from 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; import type {EnvironmentCheckerModule} from '@libs/Environment/betaChecker/types'; import type StartupTimer from '@libs/StartupTimer/types'; @@ -22,360 +16,6 @@ type RNTextInputResetModule = { }; declare module 'react-native' { - // <------ REACT NATIVE WEB (0.19.12) ------> - // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - type idRef = string; - type idRefList = idRef | idRef[]; - - // https://necolas.github.io/react-native-web/docs/accessibility/#accessibility-props-api - // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - interface AccessibilityProps { - 'aria-activedescendant'?: idRef; - 'aria-atomic'?: boolean; - 'aria-autocomplete'?: 'none' | 'list' | 'inline' | 'both'; - 'aria-busy'?: boolean; - 'aria-checked'?: boolean | 'mixed'; - 'aria-colcount'?: number; - 'aria-colindex'?: number; - 'aria-colspan'?: number; - 'aria-controls'?: idRef; - 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; - 'aria-describedby'?: idRef; - 'aria-details'?: idRef; - 'aria-disabled'?: boolean; - 'aria-errormessage'?: idRef; - 'aria-expanded'?: boolean; - 'aria-flowto'?: idRef; - 'aria-haspopup'?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; - 'aria-hidden'?: boolean; - 'aria-invalid'?: boolean; - 'aria-keyshortcuts'?: string; - 'aria-label'?: string; - 'aria-labelledby'?: idRef; - 'aria-level'?: number; - 'aria-live'?: 'assertive' | 'off' | 'polite'; - 'aria-modal'?: boolean; - 'aria-multiline'?: boolean; - 'aria-multiselectable'?: boolean; - 'aria-orientation'?: 'horizontal' | 'vertical'; - 'aria-owns'?: idRef; - 'aria-placeholder'?: string; - 'aria-posinset'?: number; - 'aria-pressed'?: boolean | 'mixed'; - 'aria-readonly'?: boolean; - 'aria-required'?: boolean; - 'aria-roledescription'?: string; - 'aria-rowcount'?: number; - 'aria-rowindex'?: number; - 'aria-rowspan'?: number; - 'aria-selected'?: boolean; - 'aria-setsize'?: number; - 'aria-sort'?: 'ascending' | 'descending' | 'none' | 'other'; - 'aria-valuemax'?: number; - 'aria-valuemin'?: number; - 'aria-valuenow'?: number; - 'aria-valuetext'?: string; - - // @deprecated - accessibilityActiveDescendant?: idRef; - accessibilityAtomic?: boolean; - accessibilityAutoComplete?: 'none' | 'list' | 'inline' | 'both'; - accessibilityBusy?: boolean; - accessibilityChecked?: boolean | 'mixed'; - accessibilityColumnCount?: number; - accessibilityColumnIndex?: number; - accessibilityColumnSpan?: number; - accessibilityControls?: idRefList; - accessibilityCurrent?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; - accessibilityDescribedBy?: idRefList; - accessibilityDetails?: idRef; - accessibilityDisabled?: boolean; - accessibilityErrorMessage?: idRef; - accessibilityExpanded?: boolean; - accessibilityFlowTo?: idRefList; - accessibilityHasPopup?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; - accessibilityHidden?: boolean; - accessibilityInvalid?: boolean; - accessibilityKeyShortcuts?: string[]; - accessibilityLabel?: string; - accessibilityLabelledBy?: idRef; - accessibilityLevel?: number; - accessibilityLiveRegion?: 'assertive' | 'none' | 'polite'; - accessibilityModal?: boolean; - accessibilityMultiline?: boolean; - accessibilityMultiSelectable?: boolean; - accessibilityOrientation?: 'horizontal' | 'vertical'; - accessibilityOwns?: idRefList; - accessibilityPlaceholder?: string; - accessibilityPosInSet?: number; - accessibilityPressed?: boolean | 'mixed'; - accessibilityReadOnly?: boolean; - accessibilityRequired?: boolean; - accessibilityRoleDescription?: string; - accessibilityRowCount?: number; - accessibilityRowIndex?: number; - accessibilityRowSpan?: number; - accessibilitySelected?: boolean; - accessibilitySetSize?: number; - accessibilitySort?: 'ascending' | 'descending' | 'none' | 'other'; - accessibilityValueMax?: number; - accessibilityValueMin?: number; - accessibilityValueNow?: number; - accessibilityValueText?: string; - } - - // https://necolas.github.io/react-native-web/docs/interactions/#pointerevent-props-api - // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js - // Extracted types from @types/react, index.d.ts - interface ClickProps { - onClick?: MouseEventHandler; - onAuxClick?: MouseEventHandler; - onContextMenu?: MouseEventHandler; - onGotPointerCapture?: PointerEventHandler; - onLostPointerCapture?: PointerEventHandler; - onPointerCancel?: PointerEventHandler; - onPointerDown?: PointerEventHandler; - onPointerEnter?: PointerEventHandler; - onPointerMove?: PointerEventHandler; - onPointerLeave?: PointerEventHandler; - onPointerOut?: PointerEventHandler; - onPointerOver?: PointerEventHandler; - onPointerUp?: PointerEventHandler; - onScroll?: UIEventHandler; - onWheel?: WheelEventHandler; - } - - // https://necolas.github.io/react-native-web/docs/interactions/#focusevent-props-api - // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js - // Extracted types from @types/react, index.d.ts - interface FocusProps { - onBlur?: FocusEventHandler; - onFocus?: FocusEventHandler; - } - - // https://necolas.github.io/react-native-web/docs/interactions/#keyboardevent-props-api - // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js - // Extracted types from @types/react, index.d.ts - interface KeyboardProps { - onKeyDown?: KeyboardEventHandler; - onKeyDownCapture?: KeyboardEventHandler; - onKeyUp?: KeyboardEventHandler; - onKeyUpCapture?: KeyboardEventHandler; - } - - // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js - // Extracted types from @types/react, index.d.ts - interface MouseProps { - onMouseDown?: MouseEventHandler; - onMouseEnter?: MouseEventHandler; - onMouseLeave?: MouseEventHandler; - onMouseMove?: MouseEventHandler; - onMouseOver?: MouseEventHandler; - onMouseOut?: MouseEventHandler; - onMouseUp?: MouseEventHandler; - } - - // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js - // Extracted types from @types/react, index.d.ts - interface TouchProps { - onTouchCancel?: TouchEventHandler; - onTouchCancelCapture?: TouchEventHandler; - onTouchEnd?: TouchEventHandler; - onTouchEndCapture?: TouchEventHandler; - onTouchMove?: TouchEventHandler; - onTouchMoveCapture?: TouchEventHandler; - onTouchStart?: TouchEventHandler; - onTouchStartCapture?: TouchEventHandler; - } - - // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api - // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js - type TouchRecord = { - currentPageX: number; - currentPageY: number; - currentTimeStamp: number; - previousPageX: number; - previousPageY: number; - previousTimeStamp: number; - startPageX: number; - startPageY: number; - startTimeStamp: number; - touchActive: boolean; - }; - - // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api - // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js - type TouchHistory = Readonly<{ - indexOfSingleActiveTouch: number; - mostRecentTimeStamp: number; - numberActiveTouches: number; - touchBank: TouchRecord[]; - }>; - - // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api - // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/createResponderEvent.js - type ResponderEvent = { - bubbles: boolean; - cancelable: boolean; - currentTarget?: unknown; // changed from "any" to "unknown" - defaultPrevented?: boolean; - dispatchConfig: { - registrationName?: string; - phasedRegistrationNames?: { - bubbled: string; - captured: string; - }; - }; - eventPhase?: number; - isDefaultPrevented: () => boolean; - isPropagationStopped: () => boolean; - isTrusted?: boolean; - preventDefault: () => void; - stopPropagation: () => void; - nativeEvent: TouchEvent; - persist: () => void; - target?: unknown; // changed from "any" to "unknown" - timeStamp: number; - touchHistory: TouchHistory; - }; - - // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api - // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderSystem.js - interface ResponderProps { - // Direct responder events dispatched directly to responder. Do not bubble. - onResponderEnd?: (e: ResponderEvent) => void; - onResponderGrant?: (e: ResponderEvent) => void | boolean; - onResponderMove?: (e: ResponderEvent) => void; - onResponderRelease?: (e: ResponderEvent) => void; - onResponderReject?: (e: ResponderEvent) => void; - onResponderStart?: (e: ResponderEvent) => void; - onResponderTerminate?: (e: ResponderEvent) => void; - onResponderTerminationRequest?: (e: ResponderEvent) => boolean; - - // On pointer down, should this element become the responder? - onStartShouldSetResponder?: (e: ResponderEvent) => boolean; - onStartShouldSetResponderCapture?: (e: ResponderEvent) => boolean; - - // On pointer move, should this element become the responder? - onMoveShouldSetResponder?: (e: ResponderEvent) => boolean; - onMoveShouldSetResponderCapture?: (e: ResponderEvent) => boolean; - - // On scroll, should this element become the responder? Do no bubble - onScrollShouldSetResponder?: (e: ResponderEvent) => boolean; - onScrollShouldSetResponderCapture?: (e: ResponderEvent) => boolean; - - // On text selection change, should this element become the responder? - onSelectionChangeShouldSetResponder?: (e: ResponderEvent) => boolean; - onSelectionChangeShouldSetResponderCapture?: (e: ResponderEvent) => boolean; - } - - // @ts-expect-error We override this type in order to provide "target" to onLayout event. - type LayoutChangeEvent = NativeSyntheticEvent<{layout: LayoutRectangle; target?: unknown}>; - - interface EventProps extends ClickProps, FocusProps, KeyboardProps, MouseProps, TouchProps, ResponderProps {} - - /** - * Shared props - * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - */ - interface WebSharedProps extends AccessibilityProps, EventProps { - dataSet?: Record; - href?: string; - hrefAttrs?: { - download?: boolean; - rel?: string; - target?: string; - }; - tabIndex?: 0 | -1; - lang?: string; - } - - /** - * View - * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - */ - interface WebViewProps extends WebSharedProps { - dir?: 'ltr' | 'rtl'; - } - interface ViewProps extends WebViewProps {} - - /** - * Text - * Extracted from react-native-web, packages/react-native-web/src/exports/Text/types.js - */ - interface WebTextProps extends WebSharedProps { - dir?: 'auto' | 'ltr' | 'rtl'; - } - interface TextProps extends WebTextProps {} - - /** - * TextInput - * Extracted from react-native-web, packages/react-native-web/src/exports/TextInput/types.js - */ - interface WebTextInputProps extends WebSharedProps { - dir?: 'auto' | 'ltr' | 'rtl'; - disabled?: boolean; - } - interface TextInputProps extends WebTextInputProps {} - - /** - * Image - * Extracted from react-native-web, packages/react-native-web/src/exports/Image/types.js - */ - interface WebImageProps extends WebSharedProps { - dir?: 'ltr' | 'rtl'; - draggable?: boolean; - } - interface ImageProps extends WebImageProps {} - - /** - * ScrollView - * Extracted from react-native-web, packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js - */ - interface WebScrollViewProps extends WebSharedProps {} - interface ScrollViewProps extends WebScrollViewProps {} - - /** - * Pressable - */ - // https://necolas.github.io/react-native-web/docs/pressable/#interactionstate - // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js - interface WebPressableStateCallbackType { - readonly focused: boolean; - readonly hovered: boolean; - readonly pressed: boolean; - } - interface PressableStateCallbackType extends WebPressableStateCallbackType {} - - // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js - interface WebPressableProps extends WebSharedProps { - /** Duration (in milliseconds) from `onPressStart` is called after pointerdown. */ - delayPressIn?: number; - /** Duration (in milliseconds) from `onPressEnd` is called after pointerup. */ - delayPressOut?: number; - /** Called when a touch is moving, after `onPressIn`. */ - onPressMove?: (event: GestureResponderEvent) => void; - } - interface PressableProps extends WebPressableProps {} - - /** - * Styles - */ - // We extend CSSProperties (alias to "csstype" library) which provides all CSS style properties for Web, - // but properties that are already defined on RN won't be overrided / augmented. - interface WebStyle extends CSSProperties { - // https://necolas.github.io/react-native-web/docs/styling/#non-standard-properties - // Exclusive to react-native-web, "pointerEvents" already included on RN - animationKeyframes?: string | Record; - writingDirection?: 'auto' | 'ltr' | 'rtl'; - enableBackground?: string; - } - - interface ViewStyle extends WebStyle {} - interface TextStyle extends WebStyle {} - interface ImageStyle extends WebStyle {} - // <------ REACT NATIVE WEB (0.19.12) ------> - interface TextInputFocusEventData extends TargetedEvent { text: string; eventCount: number; @@ -408,6 +48,7 @@ declare module 'react-native' { namespace Animated { interface AnimatedInterpolation extends AnimatedWithChildren { interpolate(config: InterpolationConfigType): AnimatedInterpolation; + // eslint-disable-next-line @typescript-eslint/naming-convention __getValue: () => OutputT; } } From d689215f97aac4bc012a8425991baa4bf113bb63 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 1 Aug 2024 18:29:51 +0200 Subject: [PATCH 2/5] Try a different approach --- src/components/BlockingViews/BlockingView.tsx | 3 ++- .../EducationalTooltip/BaseEducationalTooltip.tsx | 4 ++-- .../Tooltip/EducationalTooltip/getBounds/index.ts | 4 ++-- .../Tooltip/EducationalTooltip/getBounds/types.ts | 7 +++++-- src/types/modules/react-native-web.d.ts | 11 ----------- src/types/modules/react-native.d.ts | 1 - tsconfig.json | 2 +- 7 files changed, 12 insertions(+), 20 deletions(-) delete mode 100644 src/types/modules/react-native-web.d.ts diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx index 066aa42237a7..c2facbefbb50 100644 --- a/src/components/BlockingViews/BlockingView.tsx +++ b/src/components/BlockingViews/BlockingView.tsx @@ -1,8 +1,9 @@ import type {ImageContentFit} from 'expo-image'; import React, {useMemo} from 'react'; -import type {ImageSourcePropType, StyleProp, TextStyle, ViewStyle, WebStyle} from 'react-native'; +import type {ImageSourcePropType, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; +import type {WebStyle} from 'react-native-web'; import type {MergeExclusive} from 'type-fest'; import AutoEmailLink from '@components/AutoEmailLink'; import Icon from '@components/Icon'; diff --git a/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx b/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx index cb158150fc88..59cdf5f8c674 100644 --- a/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx +++ b/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx @@ -1,8 +1,8 @@ import React, {memo, useEffect, useRef} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; import GenericTooltip from '@components/Tooltip/GenericTooltip'; import type TooltipProps from '@components/Tooltip/types'; import getBounds from './getBounds'; +import type {LayoutChangeEventWithTarget} from './getBounds/types'; /** * A component used to wrap an element intended for displaying a tooltip. @@ -44,7 +44,7 @@ function BaseEducationalTooltip({children, ...props}: TooltipProps) { // eslint-disable-next-line react-compiler/react-compiler hideTooltipRef.current = hideTooltip; return React.cloneElement(children as React.ReactElement, { - onLayout: (e: LayoutChangeEvent) => { + onLayout: (e: LayoutChangeEventWithTarget) => { updateTargetBounds(getBounds(e)); showTooltip(); }, diff --git a/src/components/Tooltip/EducationalTooltip/getBounds/index.ts b/src/components/Tooltip/EducationalTooltip/getBounds/index.ts index d94949277740..9b54b093fde6 100644 --- a/src/components/Tooltip/EducationalTooltip/getBounds/index.ts +++ b/src/components/Tooltip/EducationalTooltip/getBounds/index.ts @@ -1,6 +1,6 @@ -import type {LayoutChangeEvent} from 'react-native'; import type GetBounds from './types'; +import type {LayoutChangeEventWithTarget} from './types'; -const getBounds: GetBounds = (event: LayoutChangeEvent) => (event.nativeEvent.target as HTMLElement).getBoundingClientRect(); +const getBounds: GetBounds = (event: LayoutChangeEventWithTarget) => event.nativeEvent.target?.getBoundingClientRect(); export default getBounds; diff --git a/src/components/Tooltip/EducationalTooltip/getBounds/types.ts b/src/components/Tooltip/EducationalTooltip/getBounds/types.ts index 081962166ff1..11779d3a21e2 100644 --- a/src/components/Tooltip/EducationalTooltip/getBounds/types.ts +++ b/src/components/Tooltip/EducationalTooltip/getBounds/types.ts @@ -1,5 +1,8 @@ -import type {LayoutChangeEvent, LayoutRectangle} from 'react-native'; +import type {LayoutRectangle, NativeSyntheticEvent} from 'react-native'; -type GetBounds = (event: LayoutChangeEvent) => LayoutRectangle; +type LayoutChangeEventWithTarget = NativeSyntheticEvent<{layout: LayoutRectangle; target: HTMLElement}>; + +type GetBounds = (event: LayoutChangeEventWithTarget) => LayoutRectangle; export default GetBounds; +export type {LayoutChangeEventWithTarget}; diff --git a/src/types/modules/react-native-web.d.ts b/src/types/modules/react-native-web.d.ts deleted file mode 100644 index f7db951eadad..000000000000 --- a/src/types/modules/react-native-web.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ -declare module 'react-native-web' { - class Clipboard { - static isAvailable(): boolean; - static getString(): Promise; - static setString(text: string): boolean; - } - - export {Clipboard}; -} diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 508c89a6e561..44ac6c243aa8 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import '@types/react-native-web'; import type {TargetedEvent} from 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; import type {EnvironmentCheckerModule} from '@libs/Environment/betaChecker/types'; diff --git a/tsconfig.json b/tsconfig.json index 16497c29b8cb..0b43522f4766 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "extends": "expo/tsconfig.base", "compilerOptions": { "module": "commonjs", - "types": ["react-native", "jest"], + "types": ["react-native", "react-native-web", "jest"], "lib": ["DOM", "DOM.Iterable", "ESNext"], "isolatedModules": true, "strict": true, From ad210d228bb880cedda7f493122403a431ae676b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 7 Aug 2024 19:57:56 +0200 Subject: [PATCH 3/5] Insall the real package --- package-lock.json | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 960f5eed7f8d..633789811e29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -189,6 +189,7 @@ "@types/react-collapse": "^5.0.1", "@types/react-dom": "^18.2.4", "@types/react-is": "^18.3.0", + "@types/react-native-web": "^0.0.0", "@types/react-test-renderer": "^18.0.0", "@types/semver": "^7.5.4", "@types/setimmediate": "^1.0.2", @@ -17851,6 +17852,16 @@ "react-native": "*" } }, + "node_modules/@types/react-native-web": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@types/react-native-web/-/react-native-web-0.0.0.tgz", + "integrity": "sha512-WeaDnb57Z60pUVu6FO6WybA+7BAbPz83otLVbOpcPvRN2f/PIDt/9ViiXJ989QFrLhdex/Jen15xMOyO2X2L2A==", + "dev": true, + "dependencies": { + "@types/react": "*", + "react-native": "*" + } + }, "node_modules/@types/react-redux": { "version": "7.1.27", "license": "MIT", diff --git a/package.json b/package.json index 1c6ba4b4b1ff..5cc9ee299f13 100644 --- a/package.json +++ b/package.json @@ -243,11 +243,11 @@ "@types/node": "^20.11.5", "@types/pusher-js": "^5.1.0", "@types/react": "18.2.45", - "@types/react-native-web": "0.73.0", "@types/react-beautiful-dnd": "^13.1.4", "@types/react-collapse": "^5.0.1", "@types/react-dom": "^18.2.4", "@types/react-is": "^18.3.0", + "@types/react-native-web": "^0.0.0", "@types/react-test-renderer": "^18.0.0", "@types/semver": "^7.5.4", "@types/setimmediate": "^1.0.2", From 0b86989235e34fd5ccc037cf1035d319f41103c4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 19 Aug 2024 12:27:38 +0200 Subject: [PATCH 4/5] Fix educational tooltip type error --- .../Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx b/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx index b5e93a1ce595..9ce1dd813825 100644 --- a/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx +++ b/src/components/Tooltip/EducationalTooltip/BaseEducationalTooltip.tsx @@ -1,10 +1,12 @@ import React, {memo, useEffect, useRef} from 'react'; import {InteractionManager} from 'react-native'; -import type {LayoutChangeEvent} from 'react-native'; +import type {LayoutRectangle, NativeSyntheticEvent} from 'react-native'; import GenericTooltip from '@components/Tooltip/GenericTooltip'; import type {EducationalTooltipProps} from '@components/Tooltip/types'; import CONST from '@src/CONST'; +type LayoutChangeEventWithTarget = NativeSyntheticEvent<{layout: LayoutRectangle; target: HTMLElement}>; + /** * A component used to wrap an element intended for displaying a tooltip. * This tooltip would show immediately without user's interaction and hide after 5 seconds. @@ -45,7 +47,7 @@ function BaseEducationalTooltip({children, shouldAutoDismiss = false, ...props}: // eslint-disable-next-line react-compiler/react-compiler hideTooltipRef.current = hideTooltip; return React.cloneElement(children as React.ReactElement, { - onLayout: (e: LayoutChangeEvent) => { + onLayout: (e: LayoutChangeEventWithTarget) => { // e.target is specific to native, use e.nativeEvent.target on web instead const target = e.target || e.nativeEvent.target; // When tooltip is used inside an animated view (e.g. popover), we need to wait for the animation to finish before measuring content. From 70747ed86cbe1ceaa3897625351cd5a8ddfcd51c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 19 Aug 2024 12:47:13 +0200 Subject: [PATCH 5/5] Remove unused import --- src/components/Composer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 16fa621604b2..a7189dbe88c7 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -4,7 +4,7 @@ import type {BaseSyntheticEvent, ForwardedRef} from 'react'; import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {flushSync} from 'react-dom'; // eslint-disable-next-line no-restricted-imports -import type {NativeSyntheticEvent, Text as RNText, TextInput, TextInputKeyPressEventData, TextInputSelectionChangeEventData, TextStyle, ViewStyle} from 'react-native'; +import type {NativeSyntheticEvent, Text as RNText, TextInput, TextInputKeyPressEventData, TextInputSelectionChangeEventData, ViewStyle} from 'react-native'; import {DeviceEventEmitter, StyleSheet, View} from 'react-native'; import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput'; import RNMarkdownTextInput from '@components/RNMarkdownTextInput';