diff --git a/.vscode/settings.json b/.vscode/settings.json index 72b33551124..08c7d493874 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,7 +100,8 @@ "xmemory": "cpp", "xtr1common": "cpp", "xtree": "cpp", - "xutility": "cpp" + "xutility": "cpp", + "xstddef": "cpp" }, "search.exclude": { "**/dist/**/*.js": true, diff --git a/change/@office-iss-react-native-win32-6b5a3f54-49cc-46ec-9e8a-a1960cf04bf5.json b/change/@office-iss-react-native-win32-6b5a3f54-49cc-46ec-9e8a-a1960cf04bf5.json new file mode 100644 index 00000000000..2413180e6b3 --- /dev/null +++ b/change/@office-iss-react-native-win32-6b5a3f54-49cc-46ec-9e8a-a1960cf04bf5.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "integrate react native nightly 7-19", + "packageName": "@office-iss/react-native-win32", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-automation-channel-9b183b6a-0435-4f5b-879b-625aea529a82.json b/change/@react-native-windows-automation-channel-9b183b6a-0435-4f5b-879b-625aea529a82.json new file mode 100644 index 00000000000..620aec5e32c --- /dev/null +++ b/change/@react-native-windows-automation-channel-9b183b6a-0435-4f5b-879b-625aea529a82.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "integrate react native nightly 7-19", + "packageName": "@react-native-windows/automation-channel", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-f8604ead-cf1e-4156-a47a-5c36478e9f1b.json b/change/react-native-windows-f8604ead-cf1e-4156-a47a-5c36478e9f1b.json new file mode 100644 index 00000000000..f00fd96281b --- /dev/null +++ b/change/react-native-windows-f8604ead-cf1e-4156-a47a-5c36478e9f1b.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "integrate react native nightly 7-19", + "packageName": "react-native-windows", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/@office-iss/react-native-win32-tester/overrides.json b/packages/@office-iss/react-native-win32-tester/overrides.json index 26e0968cdda..dc069a0aa7e 100644 --- a/packages/@office-iss/react-native-win32-tester/overrides.json +++ b/packages/@office-iss/react-native-win32-tester/overrides.json @@ -5,7 +5,7 @@ "excludePatterns": [ "src/js/examples-win32/**" ], - "baseVersion": "0.76.0-nightly-20240701-9f6cb21ed", + "baseVersion": "0.76.0-nightly-20240719-6d56cea28", "overrides": [ { "type": "patch", @@ -42,7 +42,7 @@ "type": "derived", "file": "src/js/utils/RNTesterList.win32.js", "baseFile": "packages/rn-tester/js/utils/RNTesterList.android.js", - "baseHash": "31470c217dc6b4b68de65fc0be1c072c0790cd56" + "baseHash": "587e2db8c14caac51f04005c129479d606e896a7" } ] } \ No newline at end of file diff --git a/packages/@office-iss/react-native-win32-tester/package.json b/packages/@office-iss/react-native-win32-tester/package.json index d647a51ea32..21536ca70d8 100644 --- a/packages/@office-iss/react-native-win32-tester/package.json +++ b/packages/@office-iss/react-native-win32-tester/package.json @@ -19,7 +19,7 @@ "peerDependencies": { "@office-iss/react-native-win32": "^0.0.0-canary.258", "react": "18.0.0", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed" + "react-native": "0.76.0-nightly-20240719-6d56cea28" }, "devDependencies": { "@office-iss/react-native-win32": "^0.0.0-canary.258", @@ -30,7 +30,7 @@ "@types/node": "^18.0.0", "eslint": "^8.19.0", "just-scripts": "^1.3.3", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed", + "react-native": "0.76.0-nightly-20240719-6d56cea28", "react-native-platform-override": "^1.9.45", "typescript": "5.0.4" }, diff --git a/packages/@office-iss/react-native-win32-tester/src/js/utils/RNTesterList.win32.js b/packages/@office-iss/react-native-win32-tester/src/js/utils/RNTesterList.win32.js index 84d59688264..84642d6a249 100644 --- a/packages/@office-iss/react-native-win32-tester/src/js/utils/RNTesterList.win32.js +++ b/packages/@office-iss/react-native-win32-tester/src/js/utils/RNTesterList.win32.js @@ -313,6 +313,11 @@ const APIs: Array = ([ module: require('../examples/Filter/FilterExample'), }, /* + { + key: 'MixBlendModeExample', + category: 'UI', + module: require('../examples/MixBlendMode/MixBlendModeExample'), + }, { key: 'VibrationExample', category: 'Basic', diff --git a/packages/@office-iss/react-native-win32/.flowconfig b/packages/@office-iss/react-native-win32/.flowconfig index 22c19c1cb67..30c25d65ab8 100644 --- a/packages/@office-iss/react-native-win32/.flowconfig +++ b/packages/@office-iss/react-native-win32/.flowconfig @@ -127,9 +127,7 @@ module.name_mapper='^@office-iss/react-native-win32$' -> '/index.w module.name_mapper='^react-native/\(.*\)$' -> '/\1' module.name_mapper='^@office-iss/react-native-win32/\(.*\)$' -> '\/1' module.name_mapper='^@react-native/dev-middleware$' -> '/\1' -module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/Libraries/Image/RelativeImageStub' - -one_sided_type_guards=true +module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\|xml\)$' -> '/Libraries/Image/RelativeImageStub' suppress_type=$FlowIssue suppress_type=$FlowFixMe @@ -161,4 +159,4 @@ untyped-import untyped-type-import [version] -^0.238.3 +^0.241.0 diff --git a/packages/@office-iss/react-native-win32/overrides.json b/packages/@office-iss/react-native-win32/overrides.json index 6973abb771b..83d55c51d9f 100644 --- a/packages/@office-iss/react-native-win32/overrides.json +++ b/packages/@office-iss/react-native-win32/overrides.json @@ -7,13 +7,13 @@ "**/__snapshots__/**", "src-win/rntypes/**" ], - "baseVersion": "0.76.0-nightly-20240701-9f6cb21ed", + "baseVersion": "0.76.0-nightly-20240719-6d56cea28", "overrides": [ { "type": "derived", "file": ".flowconfig", "baseFile": ".flowconfig", - "baseHash": "2b0a3c4f44b686f254516c51469500bf26c5b113" + "baseHash": "83d25eb077cc8d865039ec211f3fedeac9e57d39" }, { "type": "derived", @@ -29,7 +29,7 @@ "type": "patch", "file": "src-win/Libraries/Alert/Alert.win32.js", "baseFile": "packages/react-native/Libraries/Alert/Alert.js", - "baseHash": "897569d77df852480332b7ce7ec1b594cf40aa28" + "baseHash": "173b99e6ae120f14176cf3425877728787d3feed" }, { "type": "derived", @@ -116,7 +116,7 @@ "type": "derived", "file": "src-win/Libraries/Components/TextInput/TextInput.win32.js", "baseFile": "packages/react-native/Libraries/Components/TextInput/TextInput.js", - "baseHash": "dad569fd3dd81f212afd7b35ffe75108e926eb5e" + "baseHash": "2cc1ee09c4a71e79656db18576b4983cfca153ed" }, { "type": "patch", @@ -344,7 +344,7 @@ "type": "derived", "file": "src-win/Libraries/NativeComponent/BaseViewConfig.win32.js", "baseFile": "packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js", - "baseHash": "d7b9540d5e637cb86b29d119838a4024bfc2b7d6" + "baseHash": "86178b8f2e04b5df9e4c8dc8ca412c22231e6673" }, { "type": "copy", @@ -439,7 +439,7 @@ "type": "derived", "file": "src-win/Libraries/Text/Text.win32.js", "baseFile": "packages/react-native/Libraries/Text/Text.js", - "baseHash": "3b40ec7def4177ef81ad2eb84a0f95bc00cec041" + "baseHash": "fa9abb0db816a992824228c520fc0381bf2a1f63" }, { "type": "derived", @@ -498,13 +498,13 @@ "type": "derived", "file": "src-win/Libraries/Utilities/Platform.flow.win32.js", "baseFile": "packages/react-native/Libraries/Utilities/Platform.flow.js", - "baseHash": "d07d4656dc872e92e873bcce248262132085bd99" + "baseHash": "f38efa527b9426ae0178bb2336be2e93af0392ba" }, { "type": "derived", "file": "src-win/Libraries/Utilities/Platform.win32.js", "baseFile": "packages/react-native/Libraries/Utilities/Platform.android.js", - "baseHash": "ddd8013d71adc13b3ef3016631012efd096a9518" + "baseHash": "33f07d6fddb5290d05c8d4cc490312e3af88c70b" }, { "type": "copy", @@ -528,7 +528,7 @@ "type": "derived", "file": "src-win/src/private/specs/modules/NativePlatformConstantsWin.js", "baseFile": "packages/react-native/src/private/specs/modules/NativePlatformConstantsAndroid.js", - "baseHash": "b4a125d9134f33aeaba9e06988e18cbb0b4e9e9c" + "baseHash": "fa0f34a2de33b641bd63863629087644796d8b59" } ] } \ No newline at end of file diff --git a/packages/@office-iss/react-native-win32/package.json b/packages/@office-iss/react-native-win32/package.json index 9fd469b7703..c4b5024ff54 100644 --- a/packages/@office-iss/react-native-win32/package.json +++ b/packages/@office-iss/react-native-win32/package.json @@ -30,13 +30,13 @@ "@react-native-community/cli-platform-android": "14.0.0-alpha.11", "@react-native-community/cli-platform-ios": "14.0.0-alpha.11", "@react-native/assets": "1.0.0", - "@react-native/assets-registry": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/codegen": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/community-cli-plugin": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/gradle-plugin": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/js-polyfills": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/normalize-colors": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/virtualized-lists": "0.76.0-nightly-20240701-9f6cb21ed", + "@react-native/assets-registry": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/codegen": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/community-cli-plugin": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/gradle-plugin": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/js-polyfills": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/normalize-colors": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/virtualized-lists": "0.76.0-nightly-20240719-6d56cea28", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -57,7 +57,7 @@ "pretty-format": "^26.5.2", "promise": "^8.3.0", "react-clone-referenced-element": "^1.0.1", - "react-devtools-core": "^5.3.0", + "react-devtools-core": "^5.3.1", "react-refresh": "^0.14.0", "react-shallow-renderer": "^16.15.0", "regenerator-runtime": "^0.13.2", @@ -82,19 +82,19 @@ "@types/prop-types": "15.7.1", "@types/react": "^18.2.6", "eslint": "^8.19.0", - "flow-bin": "^0.217.2", + "flow-bin": "^0.241.0", "jscodeshift": "^0.14.0", "just-scripts": "^1.3.3", "prettier": "2.8.8", "react": "19.0.0-rc-fb9a90fa48-20240614", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed", + "react-native": "0.76.0-nightly-20240719-6d56cea28", "react-native-platform-override": "^1.9.45", "typescript": "5.0.4" }, "peerDependencies": { "@types/react": "^18.2.6", "react": "^19.0.0-rc-fb9a90fa48-20240614", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed" + "react-native": "0.76.0-nightly-20240719-6d56cea28" }, "beachball": { "defaultNpmTag": "canary", diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/Components/TextInput/TextInput.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/Components/TextInput/TextInput.win32.js index 68118dfab2a..751273329f5 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/Components/TextInput/TextInput.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/Components/TextInput/TextInput.win32.js @@ -17,6 +17,7 @@ import type { import type {ViewProps} from '../View/ViewPropTypes'; import type {TextInputType} from './TextInput.flow'; +import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags'; import usePressability from '../../Pressability/usePressability'; import flattenStyle from '../../StyleSheet/flattenStyle'; import StyleSheet, { @@ -1000,8 +1001,188 @@ export type Props = $ReadOnly<{| value?: ?Stringish, |}>; +type ViewCommands = $NonMaybeType< + | typeof AndroidTextInputCommands + | typeof RCTMultilineTextInputNativeCommands + | typeof RCTSinglelineTextInputNativeCommands, +>; + +type LastNativeSelection = {| + selection: Selection, + mostRecentEventCount: number, +|}; + const emptyFunctionThatReturnsTrue = () => true; +/** + * This hook handles the synchronization between the state of the text input + * in native and in JavaScript. This is necessary due to the asynchronous nature + * of text input events. + */ +function useTextInputStateSynchronization_STATE({ + props, + mostRecentEventCount, + selection, + inputRef, + text, + viewCommands, +}: { + props: Props, + mostRecentEventCount: number, + selection: ?Selection, + inputRef: React.RefObject>>, + text: string, + viewCommands: ViewCommands, +}): { + setLastNativeText: string => void, + setLastNativeSelection: LastNativeSelection => void, +} { + const [lastNativeText, setLastNativeText] = useState(props.value); + const [lastNativeSelectionState, setLastNativeSelection] = + useState({ + selection: {start: -1, end: -1}, + mostRecentEventCount: mostRecentEventCount, + }); + + const lastNativeSelection = lastNativeSelectionState.selection; + + // This is necessary in case native updates the text and JS decides + // that the update should be ignored and we should stick with the value + // that we have in JS. + useLayoutEffect(() => { + const nativeUpdate: {text?: string, selection?: Selection} = {}; + + if (lastNativeText !== props.value && typeof props.value === 'string') { + nativeUpdate.text = props.value; + setLastNativeText(props.value); + } + + if ( + selection && + lastNativeSelection && + (lastNativeSelection.start !== selection.start || + lastNativeSelection.end !== selection.end) + ) { + nativeUpdate.selection = selection; + setLastNativeSelection({selection, mostRecentEventCount}); + } + + if (Object.keys(nativeUpdate).length === 0) { + return; + } + + if (inputRef.current != null) { + viewCommands.setTextAndSelection( + inputRef.current, + mostRecentEventCount, + text, + selection?.start ?? -1, + selection?.end ?? -1, + ); + } + }, [ + mostRecentEventCount, + inputRef, + props.value, + props.defaultValue, + lastNativeText, + selection, + lastNativeSelection, + text, + viewCommands, + ]); + + return {setLastNativeText, setLastNativeSelection}; +} + +/** + * This hook handles the synchronization between the state of the text input + * in native and in JavaScript. This is necessary due to the asynchronous nature + * of text input events. + */ +function useTextInputStateSynchronization_REFS({ + props, + mostRecentEventCount, + selection, + inputRef, + text, + viewCommands, +}: { + props: Props, + mostRecentEventCount: number, + selection: ?Selection, + inputRef: React.RefObject>>, + text: string, + viewCommands: ViewCommands, +}): { + setLastNativeText: string => void, + setLastNativeSelection: LastNativeSelection => void, +} { + const lastNativeTextRef = useRef(props.value); + const lastNativeSelectionRef = useRef({ + selection: {start: -1, end: -1}, + mostRecentEventCount: mostRecentEventCount, + }); + + // This is necessary in case native updates the text and JS decides + // that the update should be ignored and we should stick with the value + // that we have in JS. + useLayoutEffect(() => { + const nativeUpdate: {text?: string, selection?: Selection} = {}; + + const lastNativeSelection = lastNativeSelectionRef.current.selection; + + if ( + lastNativeTextRef.current !== props.value && + typeof props.value === 'string' + ) { + nativeUpdate.text = props.value; + lastNativeTextRef.current = props.value; + } + + if ( + selection && + lastNativeSelection && + (lastNativeSelection.start !== selection.start || + lastNativeSelection.end !== selection.end) + ) { + nativeUpdate.selection = selection; + lastNativeSelectionRef.current = {selection, mostRecentEventCount}; + } + + if (Object.keys(nativeUpdate).length === 0) { + return; + } + + if (inputRef.current != null) { + viewCommands.setTextAndSelection( + inputRef.current, + mostRecentEventCount, + text, + selection?.start ?? -1, + selection?.end ?? -1, + ); + } + }, [ + mostRecentEventCount, + inputRef, + props.value, + props.defaultValue, + selection, + text, + viewCommands, + ]); + + return { + setLastNativeText: lastNativeText => { + lastNativeTextRef.current = lastNativeText; + }, + setLastNativeSelection: lastNativeSelection => { + lastNativeSelectionRef.current = lastNativeSelection; + }, + }; +} + /** * A foundational component for inputting text into the app via a * keyboard. Props provide configurability for several features, such as @@ -1134,7 +1315,6 @@ function InternalTextInput(props: Props): React.Node { const inputRef = useRef>>(null); - // eslint-disable-next-line react-hooks/exhaustive-deps const selection: ?Selection = propsSelection == null ? null @@ -1143,34 +1323,6 @@ function InternalTextInput(props: Props): React.Node { end: propsSelection.end ?? propsSelection.start, }; - const [mostRecentEventCount, setMostRecentEventCount] = useState(0); - const [lastNativeText, setLastNativeText] = useState(props.value); - const [lastNativeSelectionState, setLastNativeSelection] = useState<{| - selection: Selection, - mostRecentEventCount: number, - |}>({ - selection: {start: -1, end: -1}, - mostRecentEventCount: mostRecentEventCount, - }); - - const lastNativeSelection = lastNativeSelectionState.selection; - - let viewCommands; - if (AndroidTextInputCommands) { - viewCommands = AndroidTextInputCommands; - } - // [Windows - else if (WindowsTextInputCommands) { - viewCommands = WindowsTextInputCommands; - } - // Windows] - else { - viewCommands = - props.multiline === true - ? RCTMultilineTextInputNativeCommands - : RCTSinglelineTextInputNativeCommands; - } - const text = typeof props.value === 'string' ? props.value @@ -1178,51 +1330,26 @@ function InternalTextInput(props: Props): React.Node { ? props.defaultValue : ''; - // This is necessary in case native updates the text and JS decides - // that the update should be ignored and we should stick with the value - // that we have in JS. - useLayoutEffect(() => { - const nativeUpdate: {text?: string, selection?: Selection} = {}; - - if (lastNativeText !== props.value && typeof props.value === 'string') { - nativeUpdate.text = props.value; - setLastNativeText(props.value); - } - - if ( - selection && - lastNativeSelection && - (lastNativeSelection.start !== selection.start || - lastNativeSelection.end !== selection.end) - ) { - nativeUpdate.selection = selection; - setLastNativeSelection({selection, mostRecentEventCount}); - } + const viewCommands = + WindowsTextInputCommands || AndroidTextInputCommands || // [Windows] + (props.multiline === true + ? RCTMultilineTextInputNativeCommands + : RCTSinglelineTextInputNativeCommands) - if (Object.keys(nativeUpdate).length === 0) { - return; - } - - if (inputRef.current != null) { - viewCommands.setTextAndSelection( - inputRef.current, - mostRecentEventCount, - text, - selection?.start ?? -1, - selection?.end ?? -1, - ); - } - }, [ - mostRecentEventCount, - inputRef, - props.value, - props.defaultValue, - lastNativeText, - selection, - lastNativeSelection, - text, - viewCommands, - ]); + const [mostRecentEventCount, setMostRecentEventCount] = useState(0); + const useTextInputStateSynchronization = + ReactNativeFeatureFlags.useRefsForTextInputState() + ? useTextInputStateSynchronization_REFS + : useTextInputStateSynchronization_STATE; + const {setLastNativeText, setLastNativeSelection} = + useTextInputStateSynchronization({ + props, + inputRef, + mostRecentEventCount, + selection, + text, + viewCommands, + }); useLayoutEffect(() => { const inputRefValue = inputRef.current; @@ -1238,7 +1365,7 @@ function InternalTextInput(props: Props): React.Node { } }; } - }, [inputRef]); + }, []); const setLocalRef = useCallback( (instance: TextInputInstance | null) => { @@ -1514,12 +1641,10 @@ function InternalTextInput(props: Props): React.Node { }; } - const style = flattenStyle(props.style); - + let style = flattenStyle(props.style); if (typeof style?.fontWeight === 'number') { - // $FlowFixMe[prop-missing] - // $FlowFixMe[cannot-write] - style.fontWeight = style?.fontWeight.toString(); + // $FlowFixMe + style = [style, {fontWeight: style.fontWeight.toString()}]; } if (Platform.OS === 'ios') { diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/NativeComponent/BaseViewConfig.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/NativeComponent/BaseViewConfig.win32.js index de2200a2aa9..c70dcf2ee3c 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/NativeComponent/BaseViewConfig.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/NativeComponent/BaseViewConfig.win32.js @@ -223,6 +223,9 @@ const validAttributesForNonEventProps = { experimental_filter: { process: require('../StyleSheet/processFilter').default, }, + experimental_boxShadow: { + process: require('../StyleSheet/processBoxShadow').default, + }, borderTopWidth: true, borderTopColor: {process: require('../StyleSheet/processColor').default}, diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/Text/Text.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/Text/Text.win32.js index a850b26a077..ca13804d997 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/Text/Text.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/Text/Text.win32.js @@ -8,10 +8,11 @@ * @format */ +import type {____TextStyle_Internal as TextStyleInternal} from '../StyleSheet/StyleSheetTypes'; import type {PressEvent} from '../Types/CoreEventTypes'; -import type {TextProps} from './TextProps'; +import type {NativeTextProps} from './TextNativeComponent'; +import type {PressRetentionOffset, TextProps} from './TextProps'; -import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags'; import * as PressabilityDebug from '../Pressability/PressabilityDebug'; import usePressability from '../Pressability/usePressability'; import flattenStyle from '../StyleSheet/flattenStyle'; @@ -19,121 +20,402 @@ import processColor from '../StyleSheet/processColor'; import Platform from '../Utilities/Platform'; import TextAncestor from './TextAncestor'; import {NativeText, NativeVirtualText} from './TextNativeComponent'; -import TextOptimized from './TextOptimized'; import * as React from 'react'; import {useContext, useMemo, useState} from 'react'; +type TextForwardRef = React.ElementRef< + typeof NativeText | typeof NativeVirtualText, +>; + /** * Text is the fundamental component for displaying text. * * @see https://reactnative.dev/docs/text */ -const TextLegacy: React.AbstractComponent< - TextProps, - React.ElementRef, -> = React.forwardRef((props: TextProps, forwardedRef) => { - const { - accessible, - accessibilityControls, // Win32 - accessibilityDescribedBy, // Win32 - accessibilityDescription, // Win32 - accessibilityLabel, - accessibilityLevel, // Win32 - accessibilityPositionInSet, // Win32 - accessibilitySetSize, // Win32 - accessibilityState, - allowFontScaling, - 'aria-busy': ariaBusy, - 'aria-checked': ariaChecked, - 'aria-controls': ariaControls, // Win32 - 'aria-describedby': ariaDescribedBy, // Win32 - 'aria-description': ariaDescription, // Win32 - 'aria-disabled': ariaDisabled, - 'aria-expanded': ariaExpanded, - 'aria-label': ariaLabel, - 'aria-level': ariaLevel, // Win32 - 'aria-multiselectable': ariaMultiselectable, // Win32 - 'aria-posinset': ariaPosinset, // Win32 - 'aria-required': ariaRequired, // Win32 - 'aria-selected': ariaSelected, - 'aria-setsize': ariaSetsize, // Win32 - ellipsizeMode, - disabled, - id, - nativeID, - numberOfLines, - onLongPress, - onPress, - onPressIn, - onPressOut, - onResponderGrant, - onResponderMove, - onResponderRelease, - onResponderTerminate, - onResponderTerminationRequest, - onStartShouldSetResponder, - pressRetentionOffset, - selectable, - selectionColor, - suppressHighlighting, - style, - ...restProps - } = props; - - const [isHighlighted, setHighlighted] = useState(false); - - const _accessibilityLabel = ariaLabel ?? accessibilityLabel; - - let _accessibilityState: ?TextProps['accessibilityState'] = - accessibilityState; - if ( - ariaBusy != null || - ariaChecked != null || - ariaDisabled != null || - ariaExpanded != null || - ariaMultiselectable != null || - ariaRequired != null || - ariaSelected != null - ) { - if (_accessibilityState != null) { - _accessibilityState = { - busy: ariaBusy ?? _accessibilityState.busy, - checked: ariaChecked ?? _accessibilityState.checked, - disabled: ariaDisabled ?? _accessibilityState.disabled, - expanded: ariaExpanded ?? _accessibilityState.expanded, - multiselectable: - ariaMultiselectable ?? accessibilityState?.multiselectable, // Win32 - required: ariaRequired ?? accessibilityState?.required, // Win32 - selected: ariaSelected ?? _accessibilityState.selected, - }; - } else { - _accessibilityState = { - busy: ariaBusy, - checked: ariaChecked, - disabled: ariaDisabled, - expanded: ariaExpanded, - multiselectable: ariaMultiselectable, // Win32, - required: ariaRequired, // Win32 - selected: ariaSelected, - }; +const Text: React.AbstractComponent = + React.forwardRef( + ( + { + accessible, + accessibilityControls, // Win32 + accessibilityDescribedBy, // Win32 + accessibilityDescription, // Win32 + accessibilityLabel, + accessibilityLevel, // Win32 + accessibilityPositionInSet, // Win32 + accessibilitySetSize, // Win32 + accessibilityState, + allowFontScaling, + 'aria-busy': ariaBusy, + 'aria-checked': ariaChecked, + 'aria-controls': ariaControls, // Win32 + 'aria-describedby': ariaDescribedBy, // Win32 + 'aria-description': ariaDescription, // Win32 + 'aria-disabled': ariaDisabled, + 'aria-expanded': ariaExpanded, + 'aria-label': ariaLabel, + 'aria-level': ariaLevel, // Win32 + 'aria-multiselectable': ariaMultiselectable, // Win32 + 'aria-posinset': ariaPosinset, // Win32 + 'aria-required': ariaRequired, // Win32 + 'aria-selected': ariaSelected, + 'aria-setsize': ariaSetsize, // Win32 + children, + ellipsizeMode, + disabled, + id, + nativeID, + numberOfLines, + onLongPress, + onPress, + onPressIn, + onPressOut, + onResponderGrant, + onResponderMove, + onResponderRelease, + onResponderTerminate, + onResponderTerminationRequest, + onStartShouldSetResponder, + pressRetentionOffset, + selectable, + selectionColor, + suppressHighlighting, + style, + ...restProps + }: TextProps, + forwardedRef, + ) => { + const _accessibilityLabel = ariaLabel ?? accessibilityLabel; + const _accessibilityControls = ariaControls ?? accessibilityControls; // Win32 + const _accessibilityDescribedBy = ariaDescribedBy ?? accessibilityDescribedBy; // Win32 + const _accessibilityDescription = ariaDescription ?? accessibilityDescription; // Win32 + const _accessibilityLevel = ariaLevel ?? accessibilityLevel; // Win32 + const _accessibilityPositionInSet = ariaPosinset ?? accessibilityPositionInSet; // Win32 + const _accessibilitySetSize = ariaSetsize ?? accessibilitySetSize; // Win32 + + let _accessibilityState: ?TextProps['accessibilityState'] = + accessibilityState; + if ( + ariaBusy != null || + ariaChecked != null || + ariaDisabled != null || + ariaExpanded != null || + ariaSelected != null + ) { + if (_accessibilityState != null) { + _accessibilityState = { + busy: ariaBusy ?? _accessibilityState.busy, + checked: ariaChecked ?? _accessibilityState.checked, + disabled: ariaDisabled ?? _accessibilityState.disabled, + expanded: ariaExpanded ?? _accessibilityState.expanded, + multiselectable: ariaMultiselectable ?? accessibilityState?.multiselectable, // Win32 + required: ariaRequired ?? accessibilityState?.required, // Win32 + selected: ariaSelected ?? _accessibilityState.selected, + }; + } else { + _accessibilityState = { + busy: ariaBusy, + checked: ariaChecked, + disabled: ariaDisabled, + expanded: ariaExpanded, + multiselectable: ariaMultiselectable, // Win32, + required: ariaRequired, // Win32 + selected: ariaSelected, + }; + } + } + + const _accessibilityStateDisabled = _accessibilityState?.disabled; + const _disabled = disabled ?? _accessibilityStateDisabled; + + const isPressable = + (onPress != null || + onLongPress != null || + onStartShouldSetResponder != null) && + _disabled !== true; + + // TODO: Move this processing to the view configuration. + const _selectionColor = + selectionColor == null ? null : processColor(selectionColor); + + let _style = style; + if (__DEV__) { + if (PressabilityDebug.isEnabled() && onPress != null) { + _style = [style, {color: 'magenta'}]; + } + } + + let _numberOfLines = numberOfLines; + if (_numberOfLines != null && !(_numberOfLines >= 0)) { + if (__DEV__) { + console.error( + `'numberOfLines' in must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`, + ); + } + _numberOfLines = 0; + } + + let _selectable = selectable; + + let processedStyle: ?TextStyleInternal = flattenStyle(_style); + if (processedStyle != null) { + let overrides: ?{...TextStyleInternal} = null; + if (typeof processedStyle.fontWeight === 'number') { + overrides = overrides || ({}: {...TextStyleInternal}); + overrides.fontWeight = + // $FlowFixMe[incompatible-cast] + (processedStyle.fontWeight.toString(): TextStyleInternal['fontWeight']); + } + + if (processedStyle.userSelect != null) { + _selectable = userSelectToSelectableMap[processedStyle.userSelect]; + overrides = overrides || ({}: {...TextStyleInternal}); + overrides.userSelect = undefined; + } + + if (processedStyle.verticalAlign != null) { + overrides = overrides || ({}: {...TextStyleInternal}); + overrides.textAlignVertical = + verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign]; + overrides.verticalAlign = undefined; + } + + if (overrides != null) { + // $FlowFixMe[incompatible-type] + processedStyle = [processedStyle, overrides]; + } + } + + const _nativeID = id ?? nativeID; + + const hasTextAncestor = useContext(TextAncestor); + if (hasTextAncestor) { + if (isPressable) { + return ( + + ); + } + + return ( + + {children} + + ); + } + + // If the disabled prop and accessibilityState.disabled are out of sync but not both in + // falsy states we need to update the accessibilityState object to use the disabled prop. + if ( + _disabled !== _accessibilityStateDisabled && + ((_disabled != null && _disabled !== false) || + (_accessibilityStateDisabled != null && + _accessibilityStateDisabled !== false)) + ) { + _accessibilityState = {..._accessibilityState, disabled: _disabled}; + } + + const _accessible = Platform.select({ + ios: accessible !== false, + android: + accessible == null + ? onPress != null || onLongPress != null + : accessible, + default: accessible, + }); + + let nativeText = null; + if (isPressable) { + nativeText = ( + + ); + } else { + nativeText = ( + + {children} + + ); + } + + if (children == null) { + return nativeText; + } + + // If the children do not contain a JSX element it would not be possible to have a + // nested `Text` component so we can skip adding the `TextAncestor` context wrapper + // which has a performance overhead. Since we do this for performance reasons we need + // to keep the check simple to avoid regressing overall perf. For this reason the + // `children.length` constant is set to `3`, this should be a reasonable tradeoff + // to capture the majority of `Text` uses but also not make this check too expensive. + if (Array.isArray(children) && children.length <= 3) { + let hasNonTextChild = false; + for (let child of children) { + if (child != null && typeof child === 'object') { + hasNonTextChild = true; + break; + } + } + if (!hasNonTextChild) { + return nativeText; + } + } else if (typeof children !== 'object') { + return nativeText; + } + + return ( + {nativeText} + ); } - } + ); - const _accessibilityStateDisabled = _accessibilityState?.disabled; - const _disabled = disabled ?? _accessibilityStateDisabled; +Text.displayName = 'Text'; - const isPressable = - (onPress != null || - onLongPress != null || - onStartShouldSetResponder != null) && - _disabled !== true; +type TextPressabilityProps = $ReadOnly<{ + onLongPress?: ?(event: PressEvent) => mixed, + onPress?: ?(event: PressEvent) => mixed, + onPressIn?: ?(event: PressEvent) => mixed, + onPressOut?: ?(event: PressEvent) => mixed, + onResponderGrant?: ?(event: PressEvent) => void, + onResponderMove?: ?(event: PressEvent) => void, + onResponderRelease?: ?(event: PressEvent) => void, + onResponderTerminate?: ?(event: PressEvent) => void, + onResponderTerminationRequest?: ?() => boolean, + onStartShouldSetResponder?: ?() => boolean, + pressRetentionOffset?: ?PressRetentionOffset, + suppressHighlighting?: ?boolean, +}>; - const initialized = useLazyInitialization(isPressable); - const config = useMemo(() => { - if (!initialized) { - return null; - } +/** + * Hook that handles setting up Pressability of Text components. + * + * NOTE: This hook is relatively expensive so it should only be used absolutely necessary. + */ +function useTextPressability({ + onLongPress, + onPress, + onPressIn, + onPressOut, + onResponderGrant, + onResponderMove, + onResponderRelease, + onResponderTerminate, + onResponderTerminationRequest, + onStartShouldSetResponder, + pressRetentionOffset, + suppressHighlighting, +}: TextPressabilityProps) { + const [isHighlighted, setHighlighted] = useState(false); + // Setup pressability config and wrap callbacks needs to track the highlight state. + const config = useMemo(() => { let _onPressIn = onPressIn; let _onPressOut = onPressOut; @@ -153,7 +435,7 @@ const TextLegacy: React.AbstractComponent< } return { - disabled: !isPressable, + disabled: false, pressRectOffset: pressRetentionOffset, onLongPress, onPress, @@ -161,8 +443,6 @@ const TextLegacy: React.AbstractComponent< onPressOut: _onPressOut, }; }, [ - initialized, - isPressable, pressRetentionOffset, onLongPress, onPress, @@ -171,7 +451,10 @@ const TextLegacy: React.AbstractComponent< suppressHighlighting, ]); + // Init the pressability class const eventHandlers = usePressability(config); + + // Create NativeText event handlers which proxy events to pressability const eventHandlersForText = useMemo( () => eventHandlers == null @@ -222,140 +505,67 @@ const TextLegacy: React.AbstractComponent< ], ); - // TODO: Move this processing to the view configuration. - const _selectionColor = - selectionColor == null ? null : processColor(selectionColor); - - let _style = style; - if (__DEV__) { - if (PressabilityDebug.isEnabled() && onPress != null) { - _style = [style, {color: 'magenta'}]; - } - } - - let _numberOfLines = numberOfLines; - if (_numberOfLines != null && !(_numberOfLines >= 0)) { - if (__DEV__) { - console.error( - `'numberOfLines' in must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`, - ); - } - _numberOfLines = 0; - } - - let _selectable = selectable; - const processedStyle = flattenStyle(_style); - if (processedStyle != null) { - if (typeof processedStyle.fontWeight === 'number') { - // $FlowFixMe[cannot-write] - processedStyle.fontWeight = processedStyle.fontWeight.toString(); - } + // Return the highlight state and NativeText event handlers + return useMemo( + () => [isHighlighted, eventHandlersForText], + [isHighlighted, eventHandlersForText], + ); +} - if (processedStyle.userSelect != null) { - _selectable = userSelectToSelectableMap[processedStyle.userSelect]; - // $FlowFixMe[cannot-write] - delete processedStyle.userSelect; - } +type NativePressableTextProps = $ReadOnly<{ + textProps: NativeTextProps, + textPressabilityProps: TextPressabilityProps, +}>; - if (processedStyle.verticalAlign != null) { - // $FlowFixMe[cannot-write] - processedStyle.textAlignVertical = - verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign]; - // $FlowFixMe[cannot-write] - delete processedStyle.verticalAlign; - } - } - - const _nativeID = id ?? nativeID; - - const hasTextAncestor = useContext(TextAncestor); - if (hasTextAncestor) { - return ( - - ); - } - - // If the disabled prop and accessibilityState.disabled are out of sync but not both in - // falsy states we need to update the accessibilityState object to use the disabled prop. - if ( - _disabled !== _accessibilityStateDisabled && - ((_disabled != null && _disabled !== false) || - (_accessibilityStateDisabled != null && - _accessibilityStateDisabled !== false)) - ) { - _accessibilityState = {..._accessibilityState, disabled: _disabled}; - } - - const _accessible = Platform.select({ - ios: accessible !== false, - android: - accessible == null ? onPress != null || onLongPress != null : accessible, - default: accessible !== false, - }); +/** + * Wrap the NativeVirtualText component and initialize pressability. + * + * This logic is split out from the main Text component to enable the more + * expensive pressability logic to be only initialized when needed. + */ +const NativePressableVirtualText: React.AbstractComponent< + NativePressableTextProps, + TextForwardRef, +> = React.forwardRef(({textProps, textPressabilityProps}, forwardedRef) => { + const [isHighlighted, eventHandlersForText] = useTextPressability( + textPressabilityProps, + ); return ( - - - + ); }); -TextLegacy.displayName = 'TextLegacy'; - /** - * Returns false until the first time `newValue` is true, after which this will - * always return true. This is necessary to lazily initialize `Pressability` so - * we do not eagerly create one for every pressable `Text` component. + * Wrap the NativeText component and initialize pressability. + * + * This logic is split out from the main Text component to enable the more + * expensive pressability logic to be only initialized when needed. */ -function useLazyInitialization(newValue: boolean): boolean { - const [oldValue, setValue] = useState(newValue); - if (!oldValue && newValue) { - setValue(newValue); - } - return oldValue; -} +const NativePressableText: React.AbstractComponent< + NativePressableTextProps, + TextForwardRef, +> = React.forwardRef(({textProps, textPressabilityProps}, forwardedRef) => { + const [isHighlighted, eventHandlersForText] = useTextPressability( + textPressabilityProps, + ); + + return ( + + ); +}); const userSelectToSelectableMap = { auto: true, @@ -372,17 +582,4 @@ const verticalAlignToTextAlignVerticalMap = { middle: 'center', }; -const Text: React.AbstractComponent< - TextProps, - React.ElementRef, -> = React.forwardRef((props: TextProps, forwardedRef) => { - if (ReactNativeFeatureFlags.shouldUseOptimizedText()) { - return ; - } else { - return ; - } -}); - -Text.displayName = 'Text'; - module.exports = Text; diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.flow.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.flow.win32.js index d3f10e1ca62..5d7ec238f2b 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.flow.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.flow.win32.js @@ -34,7 +34,7 @@ type IOSPlatform = { major: number, minor: number, patch: number, - prerelease: ?string | number, + prerelease: ?string, |}, isMacCatalyst?: boolean, |}, @@ -66,7 +66,7 @@ type AndroidPlatform = { major: number, minor: number, patch: number, - prerelease: ?number, + prerelease: ?string, |}, Version: number, Release: string, @@ -105,7 +105,7 @@ type Win32Platform = { major: number, minor: number, patch: number, - prerelease: ?string | number, + prerelease: ?string, |}, systemName: string, isMacCatalyst?: boolean, diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.win32.js index ac9dbb0803a..76b9b9747fc 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/Utilities/Platform.win32.js @@ -32,7 +32,7 @@ const Platform: PlatformType = { major: number, minor: number, patch: number, - prerelease: ?string | number, + prerelease: ?string, |}, systemName: string, isMacCatalyst?: boolean, diff --git a/packages/@office-iss/react-native-win32/src-win/src/private/specs/modules/NativePlatformConstantsWin.js b/packages/@office-iss/react-native-win32/src-win/src/private/specs/modules/NativePlatformConstantsWin.js index 59b8a170cea..0a7dd5e8de7 100644 --- a/packages/@office-iss/react-native-win32/src-win/src/private/specs/modules/NativePlatformConstantsWin.js +++ b/packages/@office-iss/react-native-win32/src-win/src/private/specs/modules/NativePlatformConstantsWin.js @@ -12,6 +12,13 @@ import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; +export type ReactNativeVersionAndroid = {| + major: number, + minor: number, + patch: number, + prerelease: ?string, +|}; + export type PlatformConstantsWin32 = {| isTesting: boolean, isDisableAnimations?: boolean, @@ -19,7 +26,7 @@ export type PlatformConstantsWin32 = {| major: number, minor: number, patch: number, - prerelease: ?string | number, + prerelease: ?string, |}, forceTouchAvailable: boolean, osVersion: number, diff --git a/packages/@react-native-windows/automation-channel/package.json b/packages/@react-native-windows/automation-channel/package.json index b3bed71e35b..bd5d4f46aaa 100644 --- a/packages/@react-native-windows/automation-channel/package.json +++ b/packages/@react-native-windows/automation-channel/package.json @@ -31,7 +31,7 @@ "just-scripts": "^1.3.2", "prettier": "2.8.8", "react": "19.0.0-rc-fb9a90fa48-20240614", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed", + "react-native": "0.76.0-nightly-20240719-6d56cea28", "react-native-windows": "^0.0.0-canary.857", "typescript": "5.0.4" }, diff --git a/packages/@react-native-windows/tester/overrides.json b/packages/@react-native-windows/tester/overrides.json index 9372b869b1f..3dc1ed68117 100644 --- a/packages/@react-native-windows/tester/overrides.json +++ b/packages/@react-native-windows/tester/overrides.json @@ -5,7 +5,7 @@ "excludePatterns": [ "src/js/examples-win/**" ], - "baseVersion": "0.76.0-nightly-20240701-9f6cb21ed", + "baseVersion": "0.76.0-nightly-20240719-6d56cea28", "overrides": [ { "type": "derived", @@ -38,21 +38,21 @@ "type": "patch", "file": "src/js/examples/Image/ImageExample.windows.js", "baseFile": "packages/rn-tester/js/examples/Image/ImageExample.js", - "baseHash": "1aba10f5b2956a53348264be92ce2314d1515d28", + "baseHash": "d6fbf20539299e4830ff1da7f6f7a7b83dacae0a", "issue": 12869 }, { "type": "patch", "file": "src/js/examples/Pressable/PressableExample.windows.js", "baseFile": "packages/rn-tester/js/examples/Pressable/PressableExample.js", - "baseHash": "8e13bed9ad7453d52cc46655b4c70d95d773dd16", + "baseHash": "624fc96b5b81572b905c52518e8c5b7a2dfb78b5", "issue": 6240 }, { "type": "derived", "file": "src/js/examples/Text/TextExample.windows.js", "baseFile": "packages/rn-tester/js/examples/Text/TextExample.android.js", - "baseHash": "b6267a339a467886761a763c5b513a0381281e2f" + "baseHash": "49e1d49f57a983e08a4580163955817ca80d6f43" }, { "type": "patch", @@ -65,8 +65,7 @@ "type": "derived", "file": "src/js/examples/Touchable/TouchableExample.windows.js", "baseFile": "packages/rn-tester/js/examples/Touchable/TouchableExample.js", - "baseHash": "b91360dda9abf5e2b6f5d6d6a258c7e6f9b0b4d8", - "issue": 0 + "baseHash": "b91360dda9abf5e2b6f5d6d6a258c7e6f9b0b4d8" }, { "type": "patch", @@ -92,7 +91,7 @@ "type": "derived", "file": "src/js/utils/RNTesterList.windows.js", "baseFile": "packages/rn-tester/js/utils/RNTesterList.android.js", - "baseHash": "31470c217dc6b4b68de65fc0be1c072c0790cd56" + "baseHash": "587e2db8c14caac51f04005c129479d606e896a7" } ] } \ No newline at end of file diff --git a/packages/@react-native-windows/tester/package.json b/packages/@react-native-windows/tester/package.json index 7107a389b61..d4c5894f3b2 100644 --- a/packages/@react-native-windows/tester/package.json +++ b/packages/@react-native-windows/tester/package.json @@ -18,7 +18,7 @@ "peerDependencies": { "@react-native-picker/picker": "2.4.10", "react": "18.0.0", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed", + "react-native": "0.76.0-nightly-20240719-6d56cea28", "react-native-windows": "^0.0.0-canary.857", "react-native-xaml": "^0.0.78" }, @@ -31,7 +31,7 @@ "@types/node": "^18.0.0", "eslint": "^8.19.0", "just-scripts": "^1.3.3", - "react-native": "0.76.0-nightly-20240701-9f6cb21ed", + "react-native": "0.76.0-nightly-20240719-6d56cea28", "react-native-platform-override": "^1.9.45", "react-native-windows": "^0.0.0-canary.857", "typescript": "5.0.4" diff --git a/packages/@react-native-windows/tester/src/js/examples/Image/ImageExample.windows.js b/packages/@react-native-windows/tester/src/js/examples/Image/ImageExample.windows.js index e6dc8961610..27dc6b8de33 100644 --- a/packages/@react-native-windows/tester/src/js/examples/Image/ImageExample.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/Image/ImageExample.windows.js @@ -12,6 +12,8 @@ import type {LayoutEvent} from 'react-native/Libraries/Types/CoreEventTypes'; +import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; + const ImageCapInsetsExample = require('./ImageCapInsetsExample'); const React = require('react'); const { @@ -634,6 +636,27 @@ class OnPartialLoadExample extends React.Component< } } +type VectorDrawableExampleState = {||}; + +type VectorDrawableExampleProps = $ReadOnly<{||}>; + +class VectorDrawableExample extends React.Component< + VectorDrawableExampleProps, + VectorDrawableExampleState, +> { + state: VectorDrawableExampleState = {}; + + render(): React.Node { + const isEnabled = ReactNativeFeatureFlags.loadVectorDrawablesOnImages(); + return ( + + Enabled: {isEnabled ? 'true' : 'false'} + + + ); + } +} + const fullImage: ImageSource = { uri: IMAGE2, }; @@ -1630,4 +1653,13 @@ exports.examples = [ }, platform: 'ios', }, -]; \ No newline at end of file + { + title: 'Vector Drawable', + description: + 'Demonstrating an example of loading a vector drawable asset by name', + render: function (): React.Node { + return ; + }, + platform: 'android', + }, +]; diff --git a/packages/@react-native-windows/tester/src/js/examples/Pressable/PressableExample.windows.js b/packages/@react-native-windows/tester/src/js/examples/Pressable/PressableExample.windows.js index c1aeaf04afb..8ad93005aa2 100644 --- a/packages/@react-native-windows/tester/src/js/examples/Pressable/PressableExample.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/Pressable/PressableExample.windows.js @@ -8,6 +8,8 @@ * @flow strict-local */ +import type {RNTesterModule} from '../../types/RNTesterTypes'; + import * as React from 'react'; import { Alert, @@ -485,6 +487,9 @@ const styles = StyleSheet.create({ wrapperCustom: { borderRadius: 8, padding: 6, + width: '100%', + justifyContent: 'center', + alignItems: 'center', }, hitSlopWrapper: { backgroundColor: 'red', @@ -523,12 +528,6 @@ const styles = StyleSheet.create({ }, }); -exports.displayName = (undefined: ?string); -exports.description = 'Component for making views pressable.'; -exports.title = 'Pressable'; -exports.category = 'UI'; -exports.documentationURL = 'https://reactnative.dev/docs/pressable'; - const examples = [ { title: 'Change content based on Press', @@ -555,6 +554,33 @@ const examples = [ ); }, }, + { + title: 'Change child based on Press', + description: + ('You should be able to press the button, move your finger while pressing, and release it with the proper status updates.': string), + render(): React.Node { + return ( + + [ + { + backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white', + }, + styles.wrapperCustom, + ]}> + {({pressed}) => ( + <> + {pressed && Pressed!} + {!pressed && ( + Press me and move your finger + )} + + )} + + + ); + }, + }, { title: 'Pressable feedback events', description: (' components accept onPress, onPressIn, ' + @@ -1074,4 +1100,11 @@ if (ReactNativeFeatureFlags.shouldPressibilityUseW3CPointerEventsForHover()) { }); } -exports.examples = examples; +module.exports = ({ + title: 'Pressable', + documentationURL: 'https://reactnative.dev/docs/pressable', + category: 'UI', + description: 'Component for making views pressable.', + displayName: 'Pressable', + examples, +}: RNTesterModule); diff --git a/packages/@react-native-windows/tester/src/js/examples/Text/TextExample.windows.js b/packages/@react-native-windows/tester/src/js/examples/Text/TextExample.windows.js index 101f09cb81c..43b2dbf2735 100644 --- a/packages/@react-native-windows/tester/src/js/examples/Text/TextExample.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/Text/TextExample.windows.js @@ -21,7 +21,13 @@ import TextAdjustsDynamicLayoutExample from './TextAdjustsDynamicLayoutExample'; // import TextInlineViewsExample from './TextInlineViewsExample'; // const TextInlineView = require('../../components/TextInlineView'); const React = require('react'); -const {LayoutAnimation, StyleSheet, Text, View} = require('react-native'); +const { + LayoutAnimation, + StyleSheet, + Text, + TextInput, + View, +} = require('react-native'); class Entity extends React.Component<{|children: React.Node|}> { render(): React.Node { @@ -972,6 +978,56 @@ function TextBaseLineLayoutExample(props: {}): React.Node { {marker} + + + {'Multi-line interleaved and :'} + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris + venenatis,{' '} + + mauris eu commodo maximus + {' '} + , ante arcu vestibulum ligula, et scelerisque diam. + + + + {'Multi-line alignment'} + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + + {':'} + + {marker} + {texts} + {marker} + + + {':'} + + {marker} + + {texts} + + {marker} + */} ); diff --git a/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js b/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js index 56867e46f20..f385cb967f7 100644 --- a/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js +++ b/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js @@ -394,6 +394,11 @@ const APIs: Array = ([ module: require('../examples/Filter/FilterExample'), }, /* + { + key: 'MixBlendModeExample', + category: 'UI', + module: require('../examples/MixBlendMode/MixBlendModeExample'), + }, { key: 'VibrationExample', category: 'Basic', diff --git a/packages/@react-native/monorepo/overrides.json b/packages/@react-native/monorepo/overrides.json index f54d7780809..cb02b7df20a 100644 --- a/packages/@react-native/monorepo/overrides.json +++ b/packages/@react-native/monorepo/overrides.json @@ -1,11 +1,11 @@ { - "baseVersion": "0.76.0-nightly-20240701-9f6cb21ed", + "baseVersion": "0.76.0-nightly-20240719-6d56cea28", "overrides": [ { "type": "patch", "file": "package.json", "baseFile": "package.json", - "baseHash": "46f64f4cdede484a009bdc2ed4a0e76665b37df5" + "baseHash": "ebd6aeb9c64c8a208343e594840bd3ed57bcc3ec" } ] } \ No newline at end of file diff --git a/packages/@react-native/monorepo/package.json b/packages/@react-native/monorepo/package.json index aea514f12b8..c3358dc6a31 100644 --- a/packages/@react-native/monorepo/package.json +++ b/packages/@react-native/monorepo/package.json @@ -45,13 +45,14 @@ "@definitelytyped/dtslint": "^0.0.127", "@jest/create-cache-key-function": "^29.6.3", "@pkgjs/parseargs": "^0.11.0", - "@react-native/metro-babel-transformer": "0.76.0-nightly-20240701-9f6cb21ed", - "@react-native/metro-config": "0.76.0-nightly-20240701-9f6cb21ed", + "@react-native/metro-babel-transformer": "0.76.0-nightly-20240719-6d56cea28", + "@react-native/metro-config": "0.76.0-nightly-20240719-6d56cea28", "@tsconfig/node18": "1.0.1", "@types/react": "^18.2.6", "@typescript-eslint/parser": "^7.1.1", "ansi-styles": "^4.2.1", "babel-plugin-minify-dead-code-elimination": "^0.5.2", + "babel-plugin-syntax-hermes-parser": "0.23.0", "babel-plugin-transform-define": "^2.1.2", "babel-plugin-transform-flow-enums": "^0.0.2", "babel-preset-fbjs": "^3.4.0", @@ -72,11 +73,11 @@ "eslint-plugin-react-native": "^4.0.0", "eslint-plugin-redundant-undefined": "^0.4.0", "eslint-plugin-relay": "^1.8.3", - "flow-api-translator": "0.22.0", - "flow-bin": "^0.238.0", + "flow-api-translator": "0.23.0", + "flow-bin": "^0.241.0", "glob": "^7.1.1", - "hermes-eslint": "0.22.0", - "hermes-transform": "0.22.0", + "hermes-eslint": "0.23.0", + "hermes-transform": "0.23.0", "inquirer": "^7.1.0", "jest": "^29.6.3", "jest-junit": "^10.0.0", @@ -88,7 +89,7 @@ "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", "prettier": "2.8.8", - "prettier-plugin-hermes-parser": "0.22.0", + "prettier-plugin-hermes-parser": "0.23.0", "react": "19.0.0-rc-fb9a90fa48-20240614", "react-test-renderer": "19.0.0-rc-fb9a90fa48-20240614", "rimraf": "^3.0.2", diff --git a/packages/@react-native/tester/js/assets/alpha-hotdog.png b/packages/@react-native/tester/js/assets/alpha-hotdog.png new file mode 100644 index 00000000000..22152504d51 Binary files /dev/null and b/packages/@react-native/tester/js/assets/alpha-hotdog.png differ diff --git a/packages/@react-native/tester/js/assets/rainbow.jpeg b/packages/@react-native/tester/js/assets/rainbow.jpeg new file mode 100644 index 00000000000..170ca5b036c Binary files /dev/null and b/packages/@react-native/tester/js/assets/rainbow.jpeg differ diff --git a/packages/@react-native/tester/js/examples/Accessibility/AccessibilityIOSExample.js b/packages/@react-native/tester/js/examples/Accessibility/AccessibilityIOSExample.js index 97f2c5fa851..a40fb35f7ec 100644 --- a/packages/@react-native/tester/js/examples/Accessibility/AccessibilityIOSExample.js +++ b/packages/@react-native/tester/js/examples/Accessibility/AccessibilityIOSExample.js @@ -10,9 +10,9 @@ 'use strict'; +const {RNTesterThemeContext} = require('../../components/RNTesterTheme'); const React = require('react'); const {Alert, Text, View} = require('react-native'); -const {RNTesterThemeContext} = require('../../components/RNTesterTheme'); type Props = $ReadOnly<{||}>; class AccessibilityIOSExample extends React.Component { diff --git a/packages/@react-native/tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js b/packages/@react-native/tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js index 34c3b163b12..53369e0e917 100644 --- a/packages/@react-native/tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js +++ b/packages/@react-native/tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js @@ -11,6 +11,7 @@ 'use strict'; import type {NativeMethods} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; + import {RNTesterThemeContext} from '../../components/RNTesterTheme'; const ScreenshotManager = require('../../../NativeModuleExample/NativeScreenshotManager'); diff --git a/packages/@react-native/tester/js/examples/Alert/AlertExample.js b/packages/@react-native/tester/js/examples/Alert/AlertExample.js index b4d09721ba4..329adfc9b81 100644 --- a/packages/@react-native/tester/js/examples/Alert/AlertExample.js +++ b/packages/@react-native/tester/js/examples/Alert/AlertExample.js @@ -10,8 +10,8 @@ import type {RNTesterModule} from '../../types/RNTesterTypes'; -import * as React from 'react'; import {RNTesterThemeContext} from '../../components/RNTesterTheme'; +import * as React from 'react'; import {Alert, Pressable, StyleSheet, Text, View} from 'react-native'; // Shows log on the screen diff --git a/packages/@react-native/tester/js/examples/Animated/AnimatedIndex.js b/packages/@react-native/tester/js/examples/Animated/AnimatedIndex.js index 8fe5d815818..215efae074c 100644 --- a/packages/@react-native/tester/js/examples/Animated/AnimatedIndex.js +++ b/packages/@react-native/tester/js/examples/Animated/AnimatedIndex.js @@ -19,6 +19,7 @@ import EasingExample from './EasingExample'; import FadeInViewExample from './FadeInViewExample'; import LoopingExample from './LoopingExample'; import MovingBoxExample from './MovingBoxExample'; +import PressabilityWithNativeDrivers from './PressabilityWithNativeDrivers'; import RotatingImagesExample from './RotatingImagesExample'; import TransformBounceExample from './TransformBounceExample'; import TransformStylesExample from './TransformStylesExample'; @@ -45,5 +46,6 @@ export default ({ LoopingExample, ContinuousInteractionsExample, CombineExample, + PressabilityWithNativeDrivers, ], }: RNTesterModule); diff --git a/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js b/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js new file mode 100644 index 00000000000..e6d462ca99e --- /dev/null +++ b/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; + +import * as React from 'react'; +import {Animated, Button, Text, View} from 'react-native'; + +const componentList: number[] = Array.from({length: 100}, (_, i) => i + 1); + +function PressableWithNativeDriver() { + const currScroll = React.useRef(new Animated.Value(0)).current; + const [count, setCount] = React.useState(0); + + return ( + + +