diff --git a/change/@office-iss-react-native-win32-88707001-4b92-4fd2-ab7a-0df70ebef72d.json b/change/@office-iss-react-native-win32-88707001-4b92-4fd2-ab7a-0df70ebef72d.json new file mode 100644 index 00000000000..f9191b0ec76 --- /dev/null +++ b/change/@office-iss-react-native-win32-88707001-4b92-4fd2-ab7a-0df70ebef72d.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Integrate RN 0.85.0-nightly-20260224-42cd0f041", + "packageName": "@office-iss/react-native-win32", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-automation-channel-d5e6fc40-77a2-4d60-b399-e572636d40da.json b/change/@react-native-windows-automation-channel-d5e6fc40-77a2-4d60-b399-e572636d40da.json new file mode 100644 index 00000000000..b893294536e --- /dev/null +++ b/change/@react-native-windows-automation-channel-d5e6fc40-77a2-4d60-b399-e572636d40da.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Integrate RN 0.85.0-nightly-20260224-42cd0f041", + "packageName": "@react-native-windows/automation-channel", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-cli-3230b182-900d-460b-a6a8-5ac2db79ad0c.json b/change/@react-native-windows-cli-3230b182-900d-460b-a6a8-5ac2db79ad0c.json new file mode 100644 index 00000000000..2753db98386 --- /dev/null +++ b/change/@react-native-windows-cli-3230b182-900d-460b-a6a8-5ac2db79ad0c.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Integrate RN 0.85.0-nightly-20260224-42cd0f041", + "packageName": "@react-native-windows/cli", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-perf-testing-d29be024-6377-407f-a406-252a75532bf1.json b/change/@react-native-windows-perf-testing-d29be024-6377-407f-a406-252a75532bf1.json new file mode 100644 index 00000000000..e9e63dffbf1 --- /dev/null +++ b/change/@react-native-windows-perf-testing-d29be024-6377-407f-a406-252a75532bf1.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Integrate RN 0.85.0-nightly-20260224-42cd0f041", + "packageName": "@react-native-windows/perf-testing", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-403eff85-46dc-4313-87b2-325e03ae33e5.json b/change/react-native-windows-403eff85-46dc-4313-87b2-325e03ae33e5.json new file mode 100644 index 00000000000..4ab34b2cebd --- /dev/null +++ b/change/react-native-windows-403eff85-46dc-4313-87b2-325e03ae33e5.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Integrate RN 0.85.0-nightly-20260224-42cd0f041", + "packageName": "react-native-windows", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-init-48a8ef71-ec22-45f1-a795-97a34538cb0f.json b/change/react-native-windows-init-48a8ef71-ec22-45f1-a795-97a34538cb0f.json new file mode 100644 index 00000000000..f986efa7e32 --- /dev/null +++ b/change/react-native-windows-init-48a8ef71-ec22-45f1-a795-97a34538cb0f.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Integrate RN 0.85.0-nightly-20260224-42cd0f041", + "packageName": "react-native-windows-init", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/package.json b/package.json index b46ca0ee403..4b007a56b6e 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "prettier": "^3.6.2", "prettier-plugin-hermes-parser": "0.21.1", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-native-platform-override": "workspace:*", "typescript": "5.0.4" }, diff --git a/packages/@office-iss/react-native-win32-tester/overrides.json b/packages/@office-iss/react-native-win32-tester/overrides.json index 815159b3865..9797f80d6cf 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.85.0-nightly-20260210-4535df4f6", + "baseVersion": "0.85.0-nightly-20260224-42cd0f041", "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": "21f111f08b53ae5d60a7c03044ae64d867d93dbb" + "baseHash": "d0b6e7eccdc020d76d263b3e88becfb565b4671d" } ] } \ 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 f26ab62387c..c1c828f1ceb 100644 --- a/packages/@office-iss/react-native-win32-tester/package.json +++ b/packages/@office-iss/react-native-win32-tester/package.json @@ -11,7 +11,7 @@ "validate-overrides": "react-native-platform-override validate" }, "dependencies": { - "@react-native/tester": "0.85.0-nightly-20260210-4535df4f6", + "@react-native/tester": "0.85.0-nightly-20260224-42cd0f041", "@typescript-eslint/eslint-plugin": "^8.36.0", "@typescript-eslint/parser": "^8.36.0", "flow-enums-runtime": "^0.0.6" @@ -19,7 +19,7 @@ "peerDependencies": { "@office-iss/react-native-win32": "^0.0.0-canary.311", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6" + "react-native": "0.85.0-nightly-20260224-42cd0f041" }, "devDependencies": { "@office-iss/react-native-win32": "^0.0.0-canary.311", @@ -32,7 +32,7 @@ "just-scripts": "^1.3.3", "prettier": "^3.6.2", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-native-platform-override": "0.0.0-canary.1022", "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 d9c5c9608d4..c40b7223a32 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 @@ -186,6 +186,12 @@ const APIs: Array = ([ category: 'UI', module: require('../examples/Animated/AnimatedIndex').default, }, + { + key: 'AnimationBackendIndex', + category: 'UI', + module: require('../examples/AnimationBackend/AnimationBackendIndex') + .default, + }, { key: 'Animation - GratuitousAnimation', category: 'UI', diff --git a/packages/@office-iss/react-native-win32/.eslintignore b/packages/@office-iss/react-native-win32/.eslintignore index 6d9caf684d9..ae6cde86b63 100644 --- a/packages/@office-iss/react-native-win32/.eslintignore +++ b/packages/@office-iss/react-native-win32/.eslintignore @@ -9,6 +9,7 @@ /ReactCopies /rntypes /src +/src-win/Libraries/LogBox /src-win/rntypes /types /flow-typed diff --git a/packages/@office-iss/react-native-win32/.flowconfig b/packages/@office-iss/react-native-win32/.flowconfig index d8883742d42..bebfe7005b6 100644 --- a/packages/@office-iss/react-native-win32/.flowconfig +++ b/packages/@office-iss/react-native-win32/.flowconfig @@ -172,4 +172,4 @@ untyped-import untyped-type-import [version] -^0.299.0 +^0.302.0 diff --git a/packages/@office-iss/react-native-win32/overrides.json b/packages/@office-iss/react-native-win32/overrides.json index 5bd0e81565d..b6a6054ff90 100644 --- a/packages/@office-iss/react-native-win32/overrides.json +++ b/packages/@office-iss/react-native-win32/overrides.json @@ -7,25 +7,25 @@ "**/__snapshots__/**", "src-win/rntypes/**" ], - "baseVersion": "0.85.0-nightly-20260210-4535df4f6", + "baseVersion": "0.85.0-nightly-20260224-42cd0f041", "overrides": [ { "type": "derived", "file": ".flowconfig", "baseFile": ".flowconfig", - "baseHash": "4fee2cd64a351324f27b4ce4a99586f7e47d5dc3" + "baseHash": "baf585dd3e43f5433a04cb6b912333a0dd68b16b" }, { "type": "derived", "file": "src-win/index.win32.js", "baseFile": "packages/react-native/index.js", - "baseHash": "9007cd545c708aefc4ab3b9fb7418a210a6e879c" + "baseHash": "36c619cbe063dcd12ab4003886209d00e80ab249" }, { "type": "derived", "file": "src-win/index.win32.js.flow", "baseFile": "packages/react-native/index.js.flow", - "baseHash": "62783de689e8711d7ea287966283974956d797d5" + "baseHash": "d1e1f6a15bd8659d03fb763a86a4de0c855f05c1" }, { "type": "platform", @@ -120,7 +120,7 @@ "type": "derived", "file": "src-win/Libraries/Components/TextInput/TextInput.win32.js", "baseFile": "packages/react-native/Libraries/Components/TextInput/TextInput.js", - "baseHash": "dd89cd7044a790007febaeb8ef6c40e4c9cbdf41" + "baseHash": "cedca6e0b5e98b29759cc1ca806313ffa7937eb2" }, { "type": "patch", @@ -296,7 +296,7 @@ "type": "derived", "file": "src-win/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.win32.js", "baseFile": "packages/react-native/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js", - "baseHash": "c53eedb18ebef127f2306a42708b032ce03307c5" + "baseHash": "a40c0f275fe317de0aece23c7c26e923545c7ece" }, { "type": "patch", @@ -309,19 +309,19 @@ "type": "derived", "file": "src-win/Libraries/LogBox/UI/LogBoxInspectorReactFrames.win32.js", "baseFile": "packages/react-native/Libraries/LogBox/UI/LogBoxInspectorReactFrames.js", - "baseHash": "b495dafadab54ad429e4bf9be28d4e7ae405df56" + "baseHash": "12bd90c6572e19e7c23c0cc702d179f04dc8d7bc" }, { "type": "derived", "file": "src-win/Libraries/LogBox/UI/LogBoxInspectorSourceMapStatus.win32.js", "baseFile": "packages/react-native/Libraries/LogBox/UI/LogBoxInspectorSourceMapStatus.js", - "baseHash": "cebcc81fbc845286a04ffa5c1585155b058fd0b0" + "baseHash": "245082e81b6274f96a8da37d1e9a21fcd0ff0e18" }, { "type": "derived", "file": "src-win/Libraries/LogBox/UI/LogBoxInspectorStackFrame.win32.js", "baseFile": "packages/react-native/Libraries/LogBox/UI/LogBoxInspectorStackFrame.js", - "baseHash": "b498a05f1276dca29f4e44d0eb5fd80bcc8a4222", + "baseHash": "95e584184ae16ef23a57f7ebc3fba6b5c54a5268", "issue": 5885 }, { @@ -431,7 +431,7 @@ "type": "derived", "file": "src-win/Libraries/Text/Text.win32.js", "baseFile": "packages/react-native/Libraries/Text/Text.js", - "baseHash": "618b750401e9066b212477f3c5d5484e4d181e32" + "baseHash": "14e1085587ef8b8a506594599c1aea8cf40ef140" }, { "type": "derived", diff --git a/packages/@office-iss/react-native-win32/package.json b/packages/@office-iss/react-native-win32/package.json index 7bd4a1ab742..c0089d02d13 100644 --- a/packages/@office-iss/react-native-win32/package.json +++ b/packages/@office-iss/react-native-win32/package.json @@ -30,13 +30,14 @@ "@react-native-community/cli-platform-android": "20.0.0", "@react-native-community/cli-platform-ios": "20.0.0", "@react-native/assets": "1.0.0", - "@react-native/assets-registry": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/codegen": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/community-cli-plugin": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/gradle-plugin": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/js-polyfills": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/normalize-colors": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/virtualized-lists": "0.85.0-nightly-20260210-4535df4f6", + "@react-native/assets-registry": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/codegen": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/community-cli-plugin": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/gradle-plugin": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/jest-preset": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/js-polyfills": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/normalize-colors": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/virtualized-lists": "0.85.0-nightly-20260224-42cd0f041", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -75,7 +76,7 @@ "@babel/core": "^7.25.2", "@babel/eslint-parser": "^7.25.1", "@babel/preset-env": "^7.25.3", - "@react-native/metro-config": "0.85.0-nightly-20260210-4535df4f6", + "@react-native/metro-config": "0.85.0-nightly-20260224-42cd0f041", "@rnw-scripts/babel-react-native-config": "0.0.0", "@rnw-scripts/eslint-config": "1.2.38", "@rnw-scripts/jest-out-of-tree-snapshot-resolver": "^1.1.42", @@ -87,19 +88,19 @@ "@types/prop-types": "15.7.1", "@types/react": "^19.2.3", "eslint": "^8.19.0", - "flow-bin": "^0.299.0", + "flow-bin": "^0.302.0", "jscodeshift": "^0.14.0", "just-scripts": "^1.3.3", "prettier": "^3.6.2", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-native-platform-override": "0.0.0-canary.1022", "typescript": "5.0.4" }, "peerDependencies": { "@types/react": "^19.2.3", "react": "^19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6" + "react-native": "0.85.0-nightly-20260224-42cd0f041" }, "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 66e292e6944..a61603350b8 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 @@ -489,12 +489,11 @@ function InternalTextInput(props: TextInputProps): React.Node { getNativeRef(): ?TextInputInstance { return inputRef.current; }, - // TODO: Fix this returning true on null === null, when no input is focused isFocused(): boolean { const currentlyFocusedInput = TextInputState.currentlyFocusedInput(); return ( - currentlyFocusedInput !== null && + currentlyFocusedInput != null && currentlyFocusedInput === inputRef.current ); }, diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.win32.js index d2052fb25ab..5439c098f88 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.win32.js @@ -23,12 +23,7 @@ import LogBoxInspectorSection from './LogBoxInspectorSection'; import * as LogBoxStyle from './LogBoxStyle'; import * as React from 'react'; -type Props = Readonly<{ - componentCodeFrame: ?CodeFrame, - codeFrame: ?CodeFrame, -}>; - -function CodeFrameDisplay({codeFrame}: {codeFrame: CodeFrame}): React.Node { +component CodeFrameDisplay(codeFrame: CodeFrame) { function getFileName() { // $FlowFixMe[incompatible-use] const matches = /[^/]*$/.exec(codeFrame.fileName); @@ -77,8 +72,10 @@ function CodeFrameDisplay({codeFrame}: {codeFrame: CodeFrame}): React.Node { ); } -function LogBoxInspectorCodeFrame(props: Props): React.Node { - const {codeFrame, componentCodeFrame} = props; +component LogBoxInspectorCodeFrame( + componentCodeFrame: ?CodeFrame, + codeFrame: ?CodeFrame, +) { let sources = []; if (codeFrame != null) { sources.push(codeFrame); @@ -103,7 +100,7 @@ function LogBoxInspectorCodeFrame(props: Props): React.Node { ); } -function AppInfo() { +component AppInfo() { const appInfo = LogBoxData.getAppInfo(); if (appInfo == null) { return null; diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorReactFrames.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorReactFrames.win32.js index 8a152f778cc..29e860b6e88 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorReactFrames.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorReactFrames.win32.js @@ -21,10 +21,6 @@ import * as LogBoxStyle from './LogBoxStyle'; import * as React from 'react'; import {useState} from 'react'; -type Props = Readonly<{ - log: LogBoxLog, -}>; - const BEFORE_SLASH_RE = /^(.*)[\\/]/; // Taken from React https://github.com/facebook/react/blob/206d61f72214e8ae5b935f0bf8628491cb7f0797/packages/react-devtools-shared/src/backend/describeComponentFrame.js#L27-L41 @@ -49,29 +45,30 @@ function getPrettyFileName(path: string) { return fileName; } -function LogBoxInspectorReactFrames(props: Props): React.Node { + +component LogBoxInspectorReactFrames(log: LogBoxLog) { const [collapsed, setCollapsed] = useState(true); if ( - props.log.getAvailableComponentStack() == null || - props.log.getAvailableComponentStack().length < 1 + log.getAvailableComponentStack() == null || + log.getAvailableComponentStack().length < 1 ) { return null; } function getStackList() { if (collapsed) { - return props.log.getAvailableComponentStack().slice(0, 3); + return log.getAvailableComponentStack().slice(0, 3); } else { - return props.log.getAvailableComponentStack(); + return log.getAvailableComponentStack(); } } function getCollapseMessage() { - if (props.log.getAvailableComponentStack().length <= 3) { + if (log.getAvailableComponentStack().length <= 3) { return; } - const count = props.log.getAvailableComponentStack().length - 3; + const count = log.getAvailableComponentStack().length - 3; if (collapsed) { return `See ${count} more components`; } else { @@ -167,6 +164,7 @@ const componentStyles = StyleSheet.create({ fontFamily: Platform.select({ android: 'monospace', ios: 'Menlo', + macos: 'Menlo', win32: 'Consolas', // Win32 }), color: LogBoxStyle.getTextColor(1), @@ -178,6 +176,7 @@ const componentStyles = StyleSheet.create({ fontFamily: Platform.select({ android: 'monospace', ios: 'Menlo', + macos: 'Menlo', win32: 'Consolas', // Win32 }), color: LogBoxStyle.getTextColor(0.4), diff --git a/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorSourceMapStatus.win32.js b/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorSourceMapStatus.win32.js index a82f088eb67..9d0e2ff29f0 100644 --- a/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorSourceMapStatus.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/Libraries/LogBox/UI/LogBoxInspectorSourceMapStatus.win32.js @@ -19,12 +19,10 @@ import * as LogBoxStyle from './LogBoxStyle'; import * as React from 'react'; import {useEffect, useState} from 'react'; -type Props = Readonly<{ +component LogBoxInspectorSourceMapStatus( onPress?: ?(event: GestureResponderEvent) => void, status: 'COMPLETE' | 'FAILED' | 'NONE' | 'PENDING', -}>; - -function LogBoxInspectorSourceMapStatus(props: Props): React.Node { +) { // [Win32] Dont use Animated /* const [state, setState] = useState({ @@ -33,7 +31,7 @@ function LogBoxInspectorSourceMapStatus(props: Props): React.Node { }); useEffect(() => { - if (props.status === 'PENDING') { + if (status === 'PENDING') { if (state.animation == null) { const animated = new Animated.Value(0); const animation = Animated.loop( @@ -69,12 +67,12 @@ function LogBoxInspectorSourceMapStatus(props: Props): React.Node { state.animation.stop(); } }; - }, [props.status, state.animation]); + }, [status, state.animation]); let image; */ let color; - switch (props.status) { + switch (status) { case 'FAILED': // image = require('./LogBoxImages/alert-triangle.png'); // [win32] Dont use LogBox images color = LogBoxStyle.getErrorColor(1); @@ -87,10 +85,10 @@ function LogBoxInspectorSourceMapStatus(props: Props): React.Node { return null; // [Win32] } - if (props.status === 'COMPLETE' /* [Win32] || image == null */) { - return null; - } - + // [Win32] 'COMPLETE' and 'NONE' are already handled by the switch default + // above (which returns null), so status is narrowed to 'FAILED' | 'PENDING' + // here. The upstream `if (status === 'COMPLETE' || image == null)` check is + // therefore dead code on Win32 and is omitted. return ( {/* [Win32] Avoid Animated usage void, -}>; - -function LogBoxInspectorStackFrame(props: Props): React.Node { - const {frame, onPress} = props; +) { const column = frame.column != null && parseInt(frame.column, 10); const location = getFileName(frame.file) + @@ -93,6 +90,7 @@ const styles = StyleSheet.create({ fontFamily: Platform.select({ android: 'monospace', ios: 'Menlo', + macos: 'Menlo', win32: 'Consolas', }), }, 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 bf9cdbf3748..0f9c7e0fadf 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 @@ -14,10 +14,12 @@ import type {GestureResponderEvent} from '../Types/CoreEventTypes'; 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'; import processColor from '../StyleSheet/processColor'; +import StyleSheet from '../StyleSheet/StyleSheet'; import Platform from '../Utilities/Platform'; import TextAncestorContext from './TextAncestorContext'; import {NativeText, NativeVirtualText} from './TextNativeComponent'; @@ -246,6 +248,10 @@ const TextImpl: component( } } + if (ReactNativeFeatureFlags.defaultTextToOverflowHidden()) { + _style = [styles.default, _style]; + } + const _nativeID = id ?? nativeID; if (_accessibilityLabel !== undefined) { @@ -602,4 +608,13 @@ const verticalAlignToTextAlignVerticalMap = { middle: 'center', } as const; +const styles = StyleSheet.create({ + // Native components have historically acted like overflow: 'hidden'. We set + // this, as part of the default style, to let client differentiate with + // overflow: 'visible'. + default: { + overflow: 'hidden', + }, +}); + export default TextImpl; diff --git a/packages/@office-iss/react-native-win32/src-win/index.win32.js b/packages/@office-iss/react-native-win32/src-win/index.win32.js index 373668ae2ab..c2fcdec1c14 100644 --- a/packages/@office-iss/react-native-win32/src-win/index.win32.js +++ b/packages/@office-iss/react-native-win32/src-win/index.win32.js @@ -344,6 +344,12 @@ module.exports = { get useAnimatedValue() { return require('./Libraries/Animated/useAnimatedValue').default; }, + get useAnimatedValueXY() { + return require('./Libraries/Animated/useAnimatedValueXY').default; + }, + get useAnimatedColor() { + return require('./Libraries/Animated/useAnimatedColor').default; + }, get useColorScheme() { return require('./Libraries/Utilities/useColorScheme').default; }, diff --git a/packages/@office-iss/react-native-win32/src-win/index.win32.js.flow b/packages/@office-iss/react-native-win32/src-win/index.win32.js.flow index 2b78dddcb35..1e79e3c15f6 100644 --- a/packages/@office-iss/react-native-win32/src-win/index.win32.js.flow +++ b/packages/@office-iss/react-native-win32/src-win/index.win32.js.flow @@ -411,6 +411,8 @@ export * as TurboModuleRegistry from './Libraries/TurboModule/TurboModuleRegistr export {default as UIManager} from './Libraries/ReactNative/UIManager'; export {unstable_batchedUpdates} from './Libraries/ReactNative/RendererProxy'; export {default as useAnimatedValue} from './Libraries/Animated/useAnimatedValue'; +export {default as useAnimatedValueXY} from './Libraries/Animated/useAnimatedValueXY'; +export {default as useAnimatedColor} from './Libraries/Animated/useAnimatedColor'; export type { PressabilityConfig, EventHandlers as PressabilityEventHandlers, diff --git a/packages/@react-native-windows/automation-channel/package.json b/packages/@react-native-windows/automation-channel/package.json index ce11c513681..3a63dd1f36b 100644 --- a/packages/@react-native-windows/automation-channel/package.json +++ b/packages/@react-native-windows/automation-channel/package.json @@ -33,7 +33,7 @@ "just-scripts": "^1.3.2", "prettier": "^3.6.2", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-native-windows": "^0.0.0-canary.1056", "typescript": "5.0.4" }, diff --git a/packages/@react-native-windows/cli/package.json b/packages/@react-native-windows/cli/package.json index 6b342aae612..1e59860688b 100644 --- a/packages/@react-native-windows/cli/package.json +++ b/packages/@react-native-windows/cli/package.json @@ -66,7 +66,7 @@ "jest": "^29.7.0", "prettier": "^3.6.2", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "typescript": "5.0.4" }, "peerDependencies": { diff --git a/packages/@react-native-windows/perf-testing/package.json b/packages/@react-native-windows/perf-testing/package.json index 2327da02805..23649188ba3 100644 --- a/packages/@react-native-windows/perf-testing/package.json +++ b/packages/@react-native-windows/perf-testing/package.json @@ -36,7 +36,7 @@ "peerDependencies": { "jest": ">=29.0.3", "react": ">=18.0.0", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-test-renderer": ">=18.0.0" }, "peerDependenciesMeta": { diff --git a/packages/@react-native-windows/tester/overrides.json b/packages/@react-native-windows/tester/overrides.json index c602e8b5618..fe6a9da0de5 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.85.0-nightly-20260210-4535df4f6", + "baseVersion": "0.85.0-nightly-20260224-42cd0f041", "overrides": [ { "type": "copy", @@ -57,21 +57,21 @@ "type": "copy", "file": "src/js/examples/FlatList/FlatList-multiColumn.windows.js", "baseFile": "packages/rn-tester/js/examples/FlatList/FlatList-multiColumn.js", - "baseHash": "9d960f7a85d0b5b40e2b5a511c05364f7d952c53", + "baseHash": "13f7c3f3ddfc847cadbafa7c4137b1150f87e053", "issue": 12869 }, { "type": "patch", "file": "src/js/examples/FlatList/FlatList-nested.windows.js", "baseFile": "packages/rn-tester/js/examples/FlatList/FlatList-nested.js", - "baseHash": "58f95a964352c4ee884af4b027010fb54e4c1e39", + "baseHash": "45f9ec2ee31f7d7d1fada2709bc2ac6b54564bdf", "issue": 12869 }, { "type": "patch", "file": "src/js/examples/FlatList/FlatList-stickyHeaders.windows.js", "baseFile": "packages/rn-tester/js/examples/FlatList/FlatList-stickyHeaders.js", - "baseHash": "cfa86dc07ee3a2f17e589939a4fef982ff2c1b72" + "baseHash": "1b4aa52a4c1779607b5bd74c475dd23db152900c" }, { "type": "platform", @@ -85,7 +85,7 @@ "type": "patch", "file": "src/js/examples/Image/ImageExample.windows.js", "baseFile": "packages/rn-tester/js/examples/Image/ImageExample.js", - "baseHash": "c8ce9454827962d202cda42bd6bcd0aaceb8cf74", + "baseHash": "b119f46dbe33a567e7649f7197874d5b066403aa", "issue": 12869 }, { @@ -156,7 +156,7 @@ "type": "derived", "file": "src/js/utils/RNTesterList.windows.js", "baseFile": "packages/rn-tester/js/utils/RNTesterList.android.js", - "baseHash": "21f111f08b53ae5d60a7c03044ae64d867d93dbb" + "baseHash": "d0b6e7eccdc020d76d263b3e88becfb565b4671d" } ] } \ 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 7b252dc57f0..281a4a7d861 100644 --- a/packages/@react-native-windows/tester/package.json +++ b/packages/@react-native-windows/tester/package.json @@ -19,7 +19,7 @@ "peerDependencies": { "@react-native-picker/picker": "2.11.0", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-native-windows": "^0.0.0-canary.1056", "react-native-xaml": "^0.0.80" }, @@ -32,8 +32,8 @@ } }, "devDependencies": { - "@react-native/new-app-screen": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/tester": "0.85.0-nightly-20260210-4535df4f6", + "@react-native/new-app-screen": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/tester": "0.85.0-nightly-20260224-42cd0f041", "@rnw-scripts/babel-react-native-config": "0.0.0", "@rnw-scripts/eslint-config": "1.2.38", "@rnw-scripts/just-task": "2.3.58", @@ -43,7 +43,7 @@ "just-scripts": "^1.3.3", "prettier": "^3.6.2", "react": "19.2.3", - "react-native": "0.85.0-nightly-20260210-4535df4f6", + "react-native": "0.85.0-nightly-20260224-42cd0f041", "react-native-platform-override": "0.0.0-canary.1022", "react-native-windows": "^0.0.0-canary.1056", "typescript": "5.0.4" diff --git a/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-multiColumn.windows.js b/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-multiColumn.windows.js index 5900e5d8cde..a0b043de57f 100644 --- a/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-multiColumn.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-multiColumn.windows.js @@ -31,7 +31,7 @@ import * as React from 'react'; import {useCallback, useState} from 'react'; import {Alert, FlatList, StyleSheet, View} from 'react-native'; -function MultiColumnExample(): React.Node { +component MultiColumnExample() { const [data, setData] = useState(genNewerItems(1000)); const [filterText, setFilterText] = useState(''); const [fixedHeight, setFixedHeight] = useState(true); diff --git a/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-nested.windows.js b/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-nested.windows.js index 2e1b31d2513..38c4456841f 100644 --- a/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-nested.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-nested.windows.js @@ -76,7 +76,7 @@ function reducer(state: ItemsState, action: ItemsAction): ItemsState { return state; } -function NestedListExample(): React.Node { +component NestedListExample() { const [outer, dispatchOuter] = useReducer(reducer, initialItemsState); const [inner, dispatchInner] = useReducer(reducer, initialItemsState); const sortedInnerViewableItems = useMemo( @@ -154,18 +154,12 @@ function NestedListExample(): React.Node { ); } -function OuterItemRenderer({ - index, - item, - dispatchOuter, - dispatchInner, -}: { +component OuterItemRenderer( index: number, item: OuterItem, dispatchOuter: ItemsAction => void, dispatchInner: ItemsAction => void, - ... -}) { +) { useEffect(() => { dispatchOuter({ type: 'add-rendered', @@ -264,14 +258,7 @@ function OuterItemRenderer({ } } -function InnerItemRenderer({ - item, - dispatchInner, -}: { - item: number, - dispatchInner: ItemsAction => void, - ... -}) { +component InnerItemRenderer(item: number, dispatchInner: ItemsAction => void) { useEffect(() => { dispatchInner({ type: 'add-rendered', diff --git a/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-stickyHeaders.windows.js b/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-stickyHeaders.windows.js index 1f5dea3c947..a9c540675ed 100644 --- a/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-stickyHeaders.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/FlatList/FlatList-stickyHeaders.windows.js @@ -38,7 +38,7 @@ const Item = ({item, separators}: ListRenderItemInfo) => { ); }; -export function FlatList_stickyHeaders(): React.Node { +export component FlatList_stickyHeaders() { return ( - + React ); 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 3920bbf971b..0ee902c6dd5 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 @@ -261,6 +261,12 @@ const APIs: Array = ([ category: 'UI', module: require('../examples/Animated/AnimatedIndex').default, }, + { + key: 'AnimationBackendIndex', + category: 'UI', + module: require('../examples/AnimationBackend/AnimationBackendIndex') + .default, + }, { key: 'Animation - GratuitousAnimation', category: 'UI', diff --git a/packages/@react-native/monorepo/overrides.json b/packages/@react-native/monorepo/overrides.json index badb3209376..c313219fd44 100644 --- a/packages/@react-native/monorepo/overrides.json +++ b/packages/@react-native/monorepo/overrides.json @@ -1,11 +1,11 @@ { - "baseVersion": "0.85.0-nightly-20260210-4535df4f6", + "baseVersion": "0.85.0-nightly-20260224-42cd0f041", "overrides": [ { "type": "patch", "file": "package.json", "baseFile": "package.json", - "baseHash": "c4f1e868709a59b29acf4bcb030ab28720299f5c" + "baseHash": "835950d539e5c3124098baa7ee2cf4891b4f95f5" } ] } \ No newline at end of file diff --git a/packages/@react-native/monorepo/package.json b/packages/@react-native/monorepo/package.json index f799bddd778..110da6a1b1e 100644 --- a/packages/@react-native/monorepo/package.json +++ b/packages/@react-native/monorepo/package.json @@ -41,7 +41,7 @@ "devDependencies": { "@babel/core": "^7.25.2", "@babel/eslint-parser": "^7.25.1", - "@babel/generator": "^7.25.0", + "@babel/generator": "^7.29.1", "@babel/plugin-syntax-typescript": "^7.25.4", "@babel/plugin-transform-regenerator": "^7.24.7", "@babel/preset-env": "^7.25.3", @@ -51,8 +51,8 @@ "@jest/create-cache-key-function": "^29.7.0", "@microsoft/api-extractor": "^7.52.2", "@octokit/rest": "^22.0.0", - "@react-native/metro-babel-transformer": "0.85.0-nightly-20260210-4535df4f6", - "@react-native/metro-config": "0.85.0-nightly-20260210-4535df4f6", + "@react-native/metro-babel-transformer": "0.85.0-nightly-20260224-42cd0f041", + "@react-native/metro-config": "0.85.0-nightly-20260224-42cd0f041", "@tsconfig/node22": "22.0.2", "@types/react": "^19.2.3", "@typescript-eslint/parser": "^8.36.0", @@ -79,7 +79,7 @@ "eslint-plugin-relay": "^1.8.3", "fb-dotslash": "0.5.8", "flow-api-translator": "0.33.3", - "flow-bin": "^0.299.0", + "flow-bin": "^0.302.0", "hermes-eslint": "0.33.3", "hermes-transform": "0.33.3", "ini": "^5.0.0", diff --git a/packages/@react-native/tester/js/examples/Animated/ColorStylesExample.js b/packages/@react-native/tester/js/examples/Animated/ColorStylesExample.js index 7b44870e6c2..5abcded22ea 100644 --- a/packages/@react-native/tester/js/examples/Animated/ColorStylesExample.js +++ b/packages/@react-native/tester/js/examples/Animated/ColorStylesExample.js @@ -17,7 +17,7 @@ import * as React from 'react'; import {useState} from 'react'; import {Animated, StyleSheet, Text, View} from 'react-native'; -function AnimatedView({useNativeDriver}: {useNativeDriver: boolean}) { +component AnimatedView(useNativeDriver: boolean) { const animations = []; const animatedViewStyle = { @@ -123,7 +123,7 @@ function AnimatedView({useNativeDriver}: {useNativeDriver: boolean}) { ); } -function AnimatedColorStyleExample(): React.Node { +component AnimatedColorStyleExample() { const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/CombineExample.js b/packages/@react-native/tester/js/examples/Animated/CombineExample.js index 700dfcab230..8ee9a812085 100644 --- a/packages/@react-native/tester/js/examples/Animated/CombineExample.js +++ b/packages/@react-native/tester/js/examples/Animated/CombineExample.js @@ -24,7 +24,7 @@ export default ({ render: () => , }: RNTesterModuleExample); -const CombineExample = () => { +component CombineExample() { const [aValue, setAValue] = useState('0.4'); const [bValue, setBValue] = useState('0.5'); const a = new Animated.Value(parseFloat(aValue)); @@ -69,7 +69,7 @@ const CombineExample = () => { setAnimation(mod)}>Modulo ); -}; +} const styles = StyleSheet.create({ content: { diff --git a/packages/@react-native/tester/js/examples/Animated/ComposeAnimationsWithEasingExample.js b/packages/@react-native/tester/js/examples/Animated/ComposeAnimationsWithEasingExample.js index 5b72071a311..6808f10382f 100644 --- a/packages/@react-native/tester/js/examples/Animated/ComposeAnimationsWithEasingExample.js +++ b/packages/@react-native/tester/js/examples/Animated/ComposeAnimationsWithEasingExample.js @@ -29,7 +29,7 @@ const styles = StyleSheet.create({ }, }); -function CompositeAnimationsWithEasingExample(): React.Node { +component CompositeAnimationsWithEasingExample() { const anims = [1, 2, 3].map(() => new Animated.Value(0)); const theme = useContext(RNTesterThemeContext); diff --git a/packages/@react-native/tester/js/examples/Animated/ComposingExample.js b/packages/@react-native/tester/js/examples/Animated/ComposingExample.js index ecb6b3093fd..e94c5b4d3fc 100644 --- a/packages/@react-native/tester/js/examples/Animated/ComposingExample.js +++ b/packages/@react-native/tester/js/examples/Animated/ComposingExample.js @@ -27,7 +27,6 @@ import { useWindowDimensions, } from 'react-native'; -type Props = Readonly<{}>; const boxSize = 12; const padding = 8; const leftToRightTimingConfig = (useNativeDriver: boolean) => ({ @@ -128,12 +127,7 @@ const items = [ }, ]; -function ComposingExampleItem({ - title, - description, - compositeAnimation, - useNativeDriver, -}: { +component ComposingExampleItem( title: string, description: string, compositeAnimation: ( @@ -141,7 +135,7 @@ function ComposingExampleItem({ useNativeDriver: boolean, ) => CompositeAnimation, useNativeDriver: boolean, -}): React.Node { +) { const {width: windowWidth} = useWindowDimensions(); // Figure out how far along the x axis we should translate the box by taking into @@ -150,6 +144,7 @@ function ComposingExampleItem({ const boxIndexes = useMemo(() => [0, 1, 2, 3, 4], []); const xTranslations = useRef(boxIndexes.map(() => new Animated.Value(0))); const animation = useRef( + // $FlowFixMe[react-rule-unsafe-ref] compositeAnimation(xTranslations.current, useNativeDriver), ); const theme = useContext(RNTesterThemeContext); @@ -206,7 +201,7 @@ function ComposingExampleItem({ ); } -function ComposingExample(props: Props): React.Node { +component ComposingExample() { const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/ContinuousInteractionsExample.js b/packages/@react-native/tester/js/examples/Animated/ContinuousInteractionsExample.js index 2c4fea2dc5a..b0e7b0e1bfe 100644 --- a/packages/@react-native/tester/js/examples/Animated/ContinuousInteractionsExample.js +++ b/packages/@react-native/tester/js/examples/Animated/ContinuousInteractionsExample.js @@ -15,7 +15,7 @@ import * as React from 'react'; import {useContext} from 'react'; import {Text} from 'react-native'; -function AnimatedContinuousInteractionsExample(): React.Node { +component AnimatedContinuousInteractionsExample() { const theme = useContext(RNTesterThemeContext); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/EasingExample.js b/packages/@react-native/tester/js/examples/Animated/EasingExample.js index 46db230695c..386e12f15e4 100644 --- a/packages/@react-native/tester/js/examples/Animated/EasingExample.js +++ b/packages/@react-native/tester/js/examples/Animated/EasingExample.js @@ -23,10 +23,9 @@ import { StyleSheet, Text, View, + useAnimatedValue, } from 'react-native'; -type Props = Readonly<{}>; - type EasingListItem = { title: string, easing: (value: number) => number, @@ -80,16 +79,10 @@ const easingSections = [ }, ]; -function EasingItem({ - item, - useNativeDriver, -}: { - item: EasingListItem, - useNativeDriver: boolean, -}): React.Node { - const opacityAndScale = useRef(new Animated.Value(1)); +component EasingItem(item: EasingListItem, useNativeDriver: boolean) { + const opacityAndScale = useAnimatedValue(1); const animation = useRef( - Animated.timing(opacityAndScale.current, { + Animated.timing(opacityAndScale, { toValue: 1, duration: 1200, easing: item.easing, @@ -100,8 +93,8 @@ function EasingItem({ const animatedStyles = [ styles.box, { - opacity: opacityAndScale.current, - transform: [{scale: opacityAndScale.current}], + opacity: opacityAndScale, + transform: [{scale: opacityAndScale}], }, ]; @@ -115,7 +108,7 @@ function EasingItem({ { - opacityAndScale.current.setValue(0); + opacityAndScale.setValue(0); animation.current.start(); }}> Animate @@ -128,7 +121,7 @@ function EasingItem({ ); } -function EasingExample(props: Props): React.Node { +component EasingExample() { const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/FadeInViewExample.js b/packages/@react-native/tester/js/examples/Animated/FadeInViewExample.js index 56b8d402805..08bfe01d79f 100644 --- a/packages/@react-native/tester/js/examples/Animated/FadeInViewExample.js +++ b/packages/@react-native/tester/js/examples/Animated/FadeInViewExample.js @@ -29,13 +29,7 @@ const styles = StyleSheet.create({ }, }); -function FadeInView({ - useNativeDriver, - children, -}: { - useNativeDriver: boolean, - children: React.Node, -}) { +component FadeInView(useNativeDriver: boolean, children: React.Node) { //opacity 0 const [fadeAnim] = useState(() => new Animated.Value(0)); useEffect(() => { @@ -64,7 +58,7 @@ function FadeInView({ ); } -function FadeInExample(): React.Node { +component FadeInExample() { const [show, setShow] = useState(true); const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/LoopingExample.js b/packages/@react-native/tester/js/examples/Animated/LoopingExample.js index 4e24cf3f28e..22a6acff389 100644 --- a/packages/@react-native/tester/js/examples/Animated/LoopingExample.js +++ b/packages/@react-native/tester/js/examples/Animated/LoopingExample.js @@ -24,13 +24,7 @@ export default ({ render: () => , }: RNTesterModuleExample); -function LoopingView({ - useNativeDriver, - running, -}: { - useNativeDriver: boolean, - running: boolean, -}) { +component LoopingView(useNativeDriver: boolean, running: boolean) { const opacity = useMemo(() => new Animated.Value(1), []); const scale = useMemo(() => new Animated.Value(1), []); @@ -64,7 +58,7 @@ function LoopingView({ ); } -function LoopingExample(props: {}): React.Node { +component LoopingExample() { const [running, setRunning] = useState(false); const [useNativeDriver, setUseNativeDriver] = useState(false); diff --git a/packages/@react-native/tester/js/examples/Animated/MovingBoxExample.js b/packages/@react-native/tester/js/examples/Animated/MovingBoxExample.js index 56e9fc88925..c4044e8e06a 100644 --- a/packages/@react-native/tester/js/examples/Animated/MovingBoxExample.js +++ b/packages/@react-native/tester/js/examples/Animated/MovingBoxExample.js @@ -14,8 +14,8 @@ import RNTConfigurationBlock from '../../components/RNTConfigurationBlock'; import RNTesterButton from '../../components/RNTesterButton'; import ToggleNativeDriver from './utils/ToggleNativeDriver'; import * as React from 'react'; -import {useRef, useState} from 'react'; -import {Animated, StyleSheet, Text, View} from 'react-native'; +import {useState} from 'react'; +import {Animated, StyleSheet, Text, View, useAnimatedValue} from 'react-native'; const containerWidth = 200; const boxSize = 50; @@ -56,15 +56,13 @@ const styles = StyleSheet.create({ }, }); -type Props = Readonly<{}>; - -function MovingBoxView({useNativeDriver}: {useNativeDriver: boolean}) { - const x = useRef(new Animated.Value(0)); +component MovingBoxView(useNativeDriver: boolean) { + const x = useAnimatedValue(0); const [update, setUpdate] = useState(0); const [boxVisible, setBoxVisible] = useState(true); const moveTo = (pos: number) => { - Animated.timing(x.current, { + Animated.timing(x, { toValue: pos, duration: 1000, useNativeDriver, @@ -76,7 +74,7 @@ function MovingBoxView({useNativeDriver}: {useNativeDriver: boolean}) { }; const toggleText = boxVisible ? 'Hide' : 'Show'; const onReset = () => { - x.current.resetAnimation(); + x.resetAnimation(); setUpdate(update + 1); }; return ( @@ -85,11 +83,7 @@ function MovingBoxView({useNativeDriver}: {useNativeDriver: boolean}) { {boxVisible ? ( ) : ( The box view is not being rendered @@ -111,7 +105,7 @@ function MovingBoxView({useNativeDriver}: {useNativeDriver: boolean}) { ); } -function MovingBoxExample(props: Props): React.Node { +component MovingBoxExample() { const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/PanGestureExample.js b/packages/@react-native/tester/js/examples/Animated/PanGestureExample.js index d9745ad7677..f01b2da8560 100644 --- a/packages/@react-native/tester/js/examples/Animated/PanGestureExample.js +++ b/packages/@react-native/tester/js/examples/Animated/PanGestureExample.js @@ -22,7 +22,7 @@ import { View, } from 'react-native'; -function TextBox({children}: Readonly<{children: React.Node}>): React.Node { +component TextBox(children: React.Node) { // Prevent touch from being hijacked by Text return ( @@ -31,15 +31,13 @@ function TextBox({children}: Readonly<{children: React.Node}>): React.Node { ); } -function AnimatedEventExample({ - containerPageXY, - useNativeDriver, -}: Readonly<{ +component AnimatedEventExample( containerPageXY: Readonly<{x: number, y: number}>, useNativeDriver: boolean, -}>): React.Node { +) { const boxRef = useRef>(); + // $FlowFixMe[react-rule-unsafe-ref] const pointerPageXY = useRef( new Animated.ValueXY( { @@ -50,6 +48,7 @@ function AnimatedEventExample({ ), ).current; + // $FlowFixMe[react-rule-unsafe-ref] const dragStartOffsetXY = useRef( new Animated.ValueXY({x: 0, y: 0}, {useNativeDriver}), ).current; @@ -126,13 +125,14 @@ function AnimatedEventExample({ ); } -function PanResponderExample({ - useNativeDriver, -}: Readonly<{useNativeDriver: boolean}>): React.Node { +component PanResponderExample(useNativeDriver: boolean) { + // $FlowFixMe[react-rule-unsafe-ref] const finalOffsetXY = useRef( new Animated.ValueXY({x: 0, y: 0}, {useNativeDriver}), ).current; + // $FlowFixMe[react-rule-unsafe-ref] const dragStartOffsetXY = useRef({x: 0, y: 0}).current; + // $FlowFixMe[react-rule-unsafe-ref] const panResponder = useRef( PanResponder.create({ onMoveShouldSetPanResponder: (pressEvent, gestureState) => { @@ -177,7 +177,7 @@ function PanResponderExample({ ); } -function PanGestureExample(): React.Node { +component PanGestureExample() { const [busy, setBusy] = useState(false); const [useNativeDriver, setUseNativeDriver] = useState(false); diff --git a/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js b/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js index 2b92ca80a27..a16d3521161 100644 --- a/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js +++ b/packages/@react-native/tester/js/examples/Animated/PressabilityWithNativeDrivers.js @@ -11,13 +11,13 @@ import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; import * as React from 'react'; -import {useRef, useState} from 'react'; -import {Animated, Button, Text, View} from 'react-native'; +import {useState} from 'react'; +import {Animated, Button, Text, View, useAnimatedValue} from 'react-native'; const componentList: number[] = Array.from({length: 100}, (_, i) => i + 1); -function PressableWithNativeDriver() { - const currScroll = useRef(new Animated.Value(0)).current; +component PressableWithNativeDriver() { + const currScroll = useAnimatedValue(0); const [count, setCount] = useState(0); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/RotatingImagesExample.js b/packages/@react-native/tester/js/examples/Animated/RotatingImagesExample.js index 670ab20dff3..1db3dc145ad 100644 --- a/packages/@react-native/tester/js/examples/Animated/RotatingImagesExample.js +++ b/packages/@react-native/tester/js/examples/Animated/RotatingImagesExample.js @@ -24,7 +24,7 @@ const styles = StyleSheet.create({ }, }); -function RotatingImagesView({useNativeDriver}: {useNativeDriver: boolean}) { +component RotatingImagesView(useNativeDriver: boolean) { const anim = new Animated.Value(0); const rotatingAnimation = Animated.spring(anim, { // Returns to the start @@ -84,7 +84,7 @@ function RotatingImagesView({useNativeDriver}: {useNativeDriver: boolean}) { ); } -function RotatingImagesExample(): React.Node { +component RotatingImagesExample() { const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/TransformBounceExample.js b/packages/@react-native/tester/js/examples/Animated/TransformBounceExample.js index bed88330164..151b0ce5aa9 100644 --- a/packages/@react-native/tester/js/examples/Animated/TransformBounceExample.js +++ b/packages/@react-native/tester/js/examples/Animated/TransformBounceExample.js @@ -29,7 +29,7 @@ const styles = StyleSheet.create({ }, }); -function TransformBounceView({useNativeDriver}: {useNativeDriver: boolean}) { +component TransformBounceView(useNativeDriver: boolean) { const anim = new Animated.Value(0); const bounceAnimation = Animated.spring(anim, { // Returns to the start @@ -91,7 +91,7 @@ function TransformBounceView({useNativeDriver}: {useNativeDriver: boolean}) { ); } -function TransformBounceExample(): React.Node { +component TransformBounceExample() { const [useNativeDriver, setUseNativeDriver] = useState(false); return ( diff --git a/packages/@react-native/tester/js/examples/Animated/TransformStylesExample.js b/packages/@react-native/tester/js/examples/Animated/TransformStylesExample.js index 4168e877a57..3da1dda0421 100644 --- a/packages/@react-native/tester/js/examples/Animated/TransformStylesExample.js +++ b/packages/@react-native/tester/js/examples/Animated/TransformStylesExample.js @@ -34,13 +34,7 @@ const transformProperties = { translateY: {outputRange: [0, 100], selected: false}, }; -function AnimatedView({ - properties, - useNativeDriver, -}: { - properties: Array, - useNativeDriver: boolean, -}) { +component AnimatedView(properties: Array, useNativeDriver: boolean) { const animatedValue = new Animated.Value(0); const transformStyles = properties.map(property => ({ [property]: animatedValue.interpolate({ @@ -78,7 +72,7 @@ function AnimatedView({ ); } -function AnimatedTransformStyleExample(): React.Node { +component AnimatedTransformStyleExample() { const [properties, setProperties] = useState(transformProperties); const [useNativeDriver, setUseNativeDriver] = useState(false); const onToggle = (property: string) => diff --git a/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExSet.js b/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExSet.js index 4df8cfb6914..a4fec8a10c5 100644 --- a/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExSet.js +++ b/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExSet.js @@ -14,8 +14,15 @@ import AnExBobble from './AnExBobble'; import AnExChained from './AnExChained'; import AnExScroll from './AnExScroll'; import AnExTilt from './AnExTilt'; -import React, {useRef, useState} from 'react'; -import {Animated, PanResponder, StyleSheet, Text, View} from 'react-native'; +import React, {useState} from 'react'; +import { + Animated, + PanResponder, + StyleSheet, + Text, + View, + useAnimatedValue, +} from 'react-native'; const randColor = () => { const colors = [0, 1, 2].map(() => Math.floor(Math.random() * 150 + 100)); @@ -39,7 +46,7 @@ const AnExSet = ({ }: AnExSetProps): React.Node => { const [closeColor] = useState(randColor()); const [openColor] = useState(randColor()); - const dismissY = useRef(new Animated.Value(0)).current; + const dismissY = useAnimatedValue(0); const dismissResponder = PanResponder.create({ onStartShouldSetPanResponder: () => isActive, diff --git a/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExTilt.js b/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExTilt.js index af3130e8d98..80760d8fdaa 100644 --- a/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExTilt.js +++ b/packages/@react-native/tester/js/examples/AnimatedGratuitousApp/AnExTilt.js @@ -10,13 +10,18 @@ 'use strict'; -import React, {useCallback, useEffect, useRef} from 'react'; -import {Animated, PanResponder, StyleSheet} from 'react-native'; +import React, {useCallback, useEffect} from 'react'; +import { + Animated, + PanResponder, + StyleSheet, + useAnimatedValue, +} from 'react-native'; const AnExTilt = (): React.Node => { - const panX = useRef(new Animated.Value(0)).current; - const opacity = useRef(new Animated.Value(1)).current; - const burns = useRef(new Animated.Value(1.15)).current; + const panX = useAnimatedValue(0); + const opacity = useAnimatedValue(1); + const burns = useAnimatedValue(1.15); const tiltPanResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, diff --git a/packages/@react-native/tester/js/examples/AnimationBackend/AnimationBackendIndex.js b/packages/@react-native/tester/js/examples/AnimationBackend/AnimationBackendIndex.js new file mode 100644 index 00000000000..f8eb3d25d77 --- /dev/null +++ b/packages/@react-native/tester/js/examples/AnimationBackend/AnimationBackendIndex.js @@ -0,0 +1,29 @@ +/** + * 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 {RNTesterModule} from '../../types/RNTesterTypes'; + +import PlaygroundExample from './ChessboardExample'; +import SwipeableListExample from './SwipeableListExample'; +import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; + +const canUseBackend = + // eslint-disable-next-line + ReactNativeFeatureFlags.useSharedAnimatedBackend() && + ReactNativeFeatureFlags.cxxNativeAnimatedEnabled(); + +export default ({ + framework: 'React', + title: 'Animation Backend', + category: 'UI', + description: `Examples demonstrating the Animation Backend for layout-updating animations. ${canUseBackend ? '' : 'You need to enable c++ Animated and the Animation Backend to see these examples.'}`, + showIndividualExamples: true, + examples: canUseBackend ? [PlaygroundExample, SwipeableListExample] : [], +}: RNTesterModule); diff --git a/packages/@react-native/tester/js/examples/AnimationBackend/ChessboardExample.js b/packages/@react-native/tester/js/examples/AnimationBackend/ChessboardExample.js new file mode 100644 index 00000000000..5bf011860ba --- /dev/null +++ b/packages/@react-native/tester/js/examples/AnimationBackend/ChessboardExample.js @@ -0,0 +1,134 @@ +/** + * 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 {useMemo} from 'react'; +import {Animated, StyleSheet, Text, View, useAnimatedValue} from 'react-native'; +import {allowStyleProp} from 'react-native/Libraries/Animated/NativeAnimatedAllowlist'; + +allowStyleProp('width'); +allowStyleProp('height'); +const colors = ['lime', 'green']; + +function useLoop() { + const animatedValue = useAnimatedValue(0); + + React.useEffect(() => { + const animation = Animated.loop( + Animated.sequence([ + Animated.timing(animatedValue, { + toValue: 1, + duration: 1000, + useNativeDriver: true, + }), + Animated.timing(animatedValue, { + toValue: 0, + duration: 1000, + useNativeDriver: true, + }), + ]), + ); + animation.start(); + + return () => { + animation.stop(); + }; + }, [animatedValue]); + + return animatedValue; +} + +const N = 12; +const STATE_MAX = 30; + +function ChessboardExample() { + const [state, setState] = React.useState(0); + + const animatedValue = useLoop(); + + React.useEffect(() => { + const id = setInterval(() => { + setState(s => (s + 1) % STATE_MAX); + }, 10); + return () => { + clearInterval(id); + }; + }, []); + + const animatedStyle = useMemo(() => { + return { + width: animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [10, 30], + }), + height: animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [10, 30], + }), + }; + }, [animatedValue]); + + return ( + + {state} + + + {[...Array(N).keys()].map(i => ( + + {[...Array(N).keys()].map(j => ( + + ))} + + ))} + + + + ); +} + +const styles = StyleSheet.create({ + workaround: { + height: 400, + }, + chessboard: { + alignItems: 'flex-start', + }, + border: { + borderWidth: 10, + borderColor: 'red', + }, + row: { + flexDirection: 'row', + }, + text: { + fontSize: 20, + fontWeight: 'bold', + textAlign: 'center', + margin: 10, + }, +}); + +export default ({ + title: 'Chessboard', + name: 'chessboard', + description: 'Combine animating layout with state updates', + render: (): React.Node => , +}: RNTesterModuleExample); diff --git a/packages/@react-native/tester/js/examples/AnimationBackend/SwipeableListExample.js b/packages/@react-native/tester/js/examples/AnimationBackend/SwipeableListExample.js new file mode 100644 index 00000000000..2ede306fa51 --- /dev/null +++ b/packages/@react-native/tester/js/examples/AnimationBackend/SwipeableListExample.js @@ -0,0 +1,286 @@ +/** + * 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 {useCallback, useMemo, useRef} from 'react'; +import { + Alert, + Animated, + Dimensions, + Easing, + PanResponder, + StyleSheet, + Text, + TouchableOpacity, + View, + useAnimatedValue, +} from 'react-native'; +import {allowStyleProp} from 'react-native/Libraries/Animated/NativeAnimatedAllowlist'; + +allowStyleProp('height'); + +const windowDimensions = Dimensions.get('window'); +const BUTTON_WIDTH = 80; +const MAX_TRANSLATE = -BUTTON_WIDTH; + +type Data = { + id: string, + title: string, +}; + +const initialData: $ReadOnlyArray = [ + {id: '1', title: 'Kate Bell'}, + {id: '2', title: 'John Appleseed'}, + {id: '3', title: 'Mark Zuckerberg'}, + {id: '4', title: 'Iron Man'}, + {id: '5', title: 'Captain America'}, + {id: '6', title: 'Batman'}, + {id: '7', title: 'Matt Smith'}, +]; + +const springConfig = { + stiffness: 1000, + damping: 500, + mass: 3, + overshootClamping: true, + useNativeDriver: true, +}; + +const timingConfig = { + duration: 400, + easing: Easing.bezier(0.25, 0.1, 0.25, 1), + useNativeDriver: true, +}; + +function SwipeableListExample(): React.Node { + const [data, setData] = React.useState<$ReadOnlyArray>(initialData); + + const handleRemove = useCallback((id: string) => { + setData(currentData => currentData.filter(item => item.id !== id)); + Alert.alert('Removed'); + }, []); + + return ( + + {data.map(item => ( + handleRemove(item.id)} + /> + ))} + + ); +} + +type ListItemProps = { + item: Data, + onRemove: () => void, +}; + +function ListItem({item, onRemove}: ListItemProps): React.Node { + const isRemoving = useRef(false); + const translateX = useAnimatedValue(0); + const removalOffset = useAnimatedValue(0); // Separate value for removal animation + const height = useAnimatedValue(78); + const opacity = useAnimatedValue(1); + + const clampedTranslateX = useMemo( + () => + translateX.interpolate({ + inputRange: [MAX_TRANSLATE, 0], + outputRange: [MAX_TRANSLATE, 0], + extrapolate: 'clamp', + }), + [translateX], + ); + + // Combine clamped pan position with removal offset + const finalTranslateX = useMemo( + () => Animated.add(clampedTranslateX, removalOffset), + [clampedTranslateX, removalOffset], + ); + + const panResponder = useMemo( + () => + PanResponder.create({ + onMoveShouldSetPanResponder: (_, gestureState) => { + return Math.abs(gestureState.dx) > 10; + }, + onPanResponderGrant: () => { + translateX.extractOffset(); + }, + onPanResponderMove: Animated.event([null, {dx: translateX}], { + useNativeDriver: false, + }), + onPanResponderRelease: (_, gestureState) => { + translateX.flattenOffset(); + + const shouldOpen = gestureState.vx < -0.05; + const targetValue = shouldOpen ? MAX_TRANSLATE : 0; + + Animated.spring(translateX, { + toValue: targetValue, + velocity: gestureState.vx, + ...springConfig, + }).start(); + }, + }), + [translateX], + ); + + const handleRemove = useCallback(() => { + if (isRemoving.current) { + return; + } + isRemoving.current = true; + + // Animate removal offset separately - this bypasses the clamp + Animated.parallel([ + Animated.timing(height, { + toValue: 0, + ...timingConfig, + }), + Animated.timing(opacity, { + toValue: 0, + ...timingConfig, + }), + Animated.timing(removalOffset, { + toValue: -windowDimensions.width - MAX_TRANSLATE, + ...timingConfig, + }), + ]).start(() => { + onRemove(); + }); + }, [height, opacity, removalOffset, onRemove]); + + const animatedStyle = useMemo( + () => ({ + height, + opacity, + transform: [{translateX: finalTranslateX}], + }), + [height, opacity, finalTranslateX], + ); + + const removeButton = useMemo( + () => ({ + title: 'Delete', + backgroundColor: 'red', + color: 'white', + onPress: handleRemove, + }), + [handleRemove], + ); + + return ( + + + + +