diff --git a/change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json b/change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json new file mode 100644 index 00000000000..2064c6618e2 --- /dev/null +++ b/change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "[Fabric] Enable implementation of custom events on custom components", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithNativeLayoutNativeComponent.js b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithNativeLayoutNativeComponent.js index 9263cea8ef5..f2b4377f56f 100644 --- a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithNativeLayoutNativeComponent.js +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithNativeLayoutNativeComponent.js @@ -12,13 +12,22 @@ import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; +import type {DirectEventHandler} from 'react-native/Libraries/Types/CodegenTypes'; import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +type MyEventEvent = $ReadOnly<{| + value: boolean, + target: Int32, +|}>; + type NativeProps = $ReadOnly<{| ...ViewProps, // Props label: string, + + // Events + onMyEvent?: ?DirectEventHandler, |}>; type ComponentType = HostComponent; diff --git a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithYogaLayoutNativeComponent.js b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithYogaLayoutNativeComponent.js index 6dbddb8b913..8db280fb02b 100644 --- a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithYogaLayoutNativeComponent.js +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/CustomXamlComponentWithYogaLayoutNativeComponent.js @@ -12,13 +12,22 @@ import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; +import type {DirectEventHandler} from 'react-native/Libraries/Types/CodegenTypes'; import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +type MyEventEvent = $ReadOnly<{| + value: boolean, + target: Int32, +|}>; + type NativeProps = $ReadOnly<{| ...ViewProps, // Props label: string, + + // Events + onMyEvent?: ?DirectEventHandler, |}>; type ComponentType = HostComponent; diff --git a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponent.windows.js b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponent.windows.js index 1f259b42f2c..ae9b97c19a8 100644 --- a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponent.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponent.windows.js @@ -10,10 +10,43 @@ 'use strict'; -import React from 'react'; +import React, { useState } from 'react'; import {Text, View} from 'react-native'; import CustomXamlComponentWithNativeLayout from './CustomXamlComponentWithNativeLayoutNativeComponent'; +const NativeComponentWithNativeLayout = () => { + let [log, setLog] = useState(''); + return ( + + + + + + + This is RN Text + {log} + {setLog(log + '\nRecieved MyEvent: ' + JSON.stringify(arg.nativeEvent) + '\n')}} + /> + + + + + + + ); +} + exports.displayName = 'NativeFabricComponent'; exports.framework = 'React'; exports.category = 'UI'; @@ -27,32 +60,8 @@ exports.examples = [ title: 'Native Component', render: function (): React.Node { return ( - - - - - - - This is RN Text - - - - - - - + ); }, - }, + } ]; diff --git a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponentYoga.windows.js b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponentYoga.windows.js index 7275f9c76af..1d5193d75fd 100644 --- a/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponentYoga.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativeComponents/NativeComponentYoga.windows.js @@ -10,10 +10,43 @@ 'use strict'; -import React from 'react'; +import React, { useState } from 'react'; import {Text, View} from 'react-native'; import CustomXamlComponentWithYogaLayout from './CustomXamlComponentWithYogaLayoutNativeComponent'; +const NativeComponentWithYogaExample = () => { + let [log, setLog] = useState(''); + return ( + + + + + + + This is RN Text + {log} + {setLog(log + '\nRecieved MyEvent: ' + JSON.stringify(arg.nativeEvent) + '\n')}} + /> + + + + + + + ); +}; + exports.displayName = 'NativeFabricComponentYoga'; exports.framework = 'React'; exports.category = 'UI'; @@ -27,31 +60,7 @@ exports.examples = [ title: 'Native Component', render: function (): React.Node { return ( - - - - - - - This is RN Text - - - - - - - + ); }, }, diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap index f621649f83e..9e7aff60327 100644 --- a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap +++ b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap @@ -10550,8 +10550,16 @@ exports[`snapshotAllPages Fabric Native Component 1`] = ` > This is RN Text + This is RN Text + { void Initialize( const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView, @@ -67,6 +92,14 @@ struct CustomComponentUserData : winrt::implementsonMyEvent(args); + } + } + winrt::Microsoft::UI::Xaml::UIElement CreateXamlButtonContent(bool nativeLayout) { m_buttonLabelTextBlock = winrt::Microsoft::UI::Xaml::Controls::TextBlock(); m_buttonLabelTextBlock.Text(L"This is a Xaml Button set to ellipisify on truncation"); @@ -103,94 +136,108 @@ struct CustomComponentUserData : winrt::implements(props); + }); -void ConfigureBuilderForCustomComponent( - winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder, - bool nativeLayout) { - builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props) noexcept { - return winrt::make(props); - }); - auto compBuilder = builder.as(); + builder.SetFinalizeUpdateHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) { + auto userData = source.UserData().as(); + userData->FinalizeUpdates(); + }); - compBuilder.SetContentIslandComponentViewInitializer( - [nativeLayout]( - const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept { - auto userData = winrt::make_self(); - userData->Initialize(islandView, nativeLayout); + auto compBuilder = builder.as(); + + compBuilder.SetContentIslandComponentViewInitializer( + [nativeLayout]( + const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept { + auto userData = winrt::make_self(); + userData->Initialize(islandView, nativeLayout); + islandView.UserData(*userData); #ifdef USE_EXPERIMENTAL_WINUI3 - islandView.Destroying([](const winrt::IInspectable &sender, const winrt::IInspectable &args) { - auto senderIslandView = sender.as(); - auto userData = senderIslandView.UserData().as(); - userData->m_xamlIsland.Close(); - }); + islandView.Destroying([](const winrt::IInspectable &sender, const winrt::IInspectable &args) { + auto senderIslandView = sender.as(); + auto userData = senderIslandView.UserData().as(); + userData->m_xamlIsland.Close(); + }); #endif + }); + + builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) { + auto senderIslandView = source.as(); + auto userData = senderIslandView.UserData().as(); + userData->m_eventEmitter = std::make_unique(eventEmitter); + }); + + if (nativeLayout) { + builder.SetMeasureContentHandler([](winrt::Microsoft::ReactNative::ShadowNode shadowNode, + winrt::Microsoft::ReactNative::LayoutContext layoutContext, + winrt::Microsoft::ReactNative::LayoutConstraints layoutContraints) noexcept { + shadowNode.Tag(winrt::box_value(layoutContraints)); + + auto currentState = winrt::get_self(shadowNode.StateData()); + + if (currentState) { + // Snap up to the nearest whole pixel to avoid pixel snapping truncations + auto size = winrt::Windows::Foundation::Size{ + std::ceil(currentState->desiredSize.Width * layoutContext.PointScaleFactor()) / + layoutContext.PointScaleFactor() + + 1.0f, + std::ceil(currentState->desiredSize.Height * layoutContext.PointScaleFactor()) / + layoutContext.PointScaleFactor() + + 1.0f, + }; + return size; + } + + return winrt::Windows::Foundation::Size{0, 0}; }); + builder.SetInitialStateDataFactory([](const winrt::Microsoft::ReactNative::IComponentProps & /*props*/) noexcept { + return winrt::make(winrt::Windows::Foundation::Size{0, 0}); + }); + } - if (nativeLayout) { - builder.SetMeasureContentHandler([](winrt::Microsoft::ReactNative::ShadowNode shadowNode, - winrt::Microsoft::ReactNative::LayoutContext layoutContext, - winrt::Microsoft::ReactNative::LayoutConstraints layoutContraints) noexcept { - shadowNode.Tag(winrt::box_value(layoutContraints)); - - auto currentState = winrt::get_self(shadowNode.StateData()); - - if (currentState) { - // Snap up to the nearest whole pixel to avoid pixel snapping truncations - auto size = winrt::Windows::Foundation::Size{ - std::ceil(currentState->desiredSize.Width * layoutContext.PointScaleFactor()) / - layoutContext.PointScaleFactor() + - 1.0f, - std::ceil(currentState->desiredSize.Height * layoutContext.PointScaleFactor()) / - layoutContext.PointScaleFactor() + - 1.0f, - }; - return size; - } - - return winrt::Windows::Foundation::Size{0, 0}; + builder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + const winrt::Microsoft::ReactNative::IComponentProps &newProps, + const winrt::Microsoft::ReactNative::IComponentProps &oldProps) { + auto senderIslandView = source.as(); + auto userData = senderIslandView.UserData().as(); + userData->PropsChanged(senderIslandView, newProps, oldProps); }); - builder.SetInitialStateDataFactory([](const winrt::Microsoft::ReactNative::IComponentProps & /*props*/) noexcept { - return winrt::make(winrt::Windows::Foundation::Size{0, 0}); + + builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + const winrt::Microsoft::ReactNative::IComponentState &newState) { + auto senderIslandView = source.as(); + auto userData = senderIslandView.UserData().as(); + userData->m_state = newState; }); } + private: + winrt::Microsoft::UI::Xaml::Controls::TextBlock m_buttonLabelTextBlock{nullptr}; + winrt::Microsoft::ReactNative::IComponentState m_state; + std::unique_ptr m_eventEmitter{nullptr}; #ifdef USE_EXPERIMENTAL_WINUI3 - compBuilder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, - const winrt::Microsoft::ReactNative::IComponentProps &newProps, - const winrt::Microsoft::ReactNative::IComponentProps &oldProps) { - auto senderIslandView = source.as(); - auto userData = senderIslandView.UserData().as(); - userData->PropsChanged(senderIslandView, newProps, oldProps); - }); - - compBuilder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, - const winrt::Microsoft::ReactNative::IComponentState &newState) { - auto senderIslandView = source.as(); - auto userData = senderIslandView.UserData().as(); - userData->m_state = newState; - }); + winrt::Microsoft::UI::Xaml::XamlIsland m_xamlIsland{nullptr}; #endif -} +}; static void RegisterViewComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) { packageBuilder.as().AddViewComponent( L"CustomXamlComponentWithNativeLayout", [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { - ConfigureBuilderForCustomComponent(builder, true /*nativeLayout*/); + CustomComponentUserData::ConfigureBuilderForCustomComponent(builder, true /*nativeLayout*/); }); packageBuilder.as().AddViewComponent( L"CustomXamlComponentWithYogaLayout", [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { - ConfigureBuilderForCustomComponent(builder, false /*nativeLayout*/); + CustomComponentUserData::ConfigureBuilderForCustomComponent(builder, false /*nativeLayout*/); }); } } // namespace winrt::PlaygroundApp::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp new file mode 100644 index 00000000000..0cbbaa32526 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AbiEventEmitter.h" +#include "JsiWriter.h" + +namespace winrt::Microsoft::ReactNative::implementation { + +EventEmitter::EventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) : m_eventEmitter(eventEmitter) {} + +void EventEmitter::DispatchEvent( + winrt::hstring eventName, + const winrt::Microsoft::ReactNative::JSValueArgWriter &args) { + m_eventEmitter->dispatchEvent(winrt::to_string(eventName), [args](facebook::jsi::Runtime &runtime) { + auto writer = winrt::make(runtime); + args(writer); + return winrt::get_self(writer)->MoveResult(); + }); +} + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h new file mode 100644 index 00000000000..2daa0986947 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "ViewProps.g.h" + +#include "EventEmitter.g.h" +#include +#include + +namespace winrt::Microsoft::ReactNative::implementation { + +struct EventEmitter : EventEmitterT { + EventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter); + + void DispatchEvent(winrt::hstring eventName, const winrt::Microsoft::ReactNative::JSValueArgWriter &args); + + private: + facebook::react::EventEmitter::Shared const m_eventEmitter; +}; + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp index 34ff1adb230..d35d25104f2 100644 --- a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp @@ -65,6 +65,13 @@ void ShadowNode::StateData(winrt::IInspectable tag) noexcept { } } +winrt::Microsoft::ReactNative::EventEmitter ShadowNode::EventEmitter() const noexcept { + if (auto shadowNode = m_shadowNode.lock()) { + return winrt::make(shadowNode->getEventEmitter()); + } + return nullptr; +} + } // namespace winrt::Microsoft::ReactNative::implementation namespace Microsoft::ReactNative { diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h index a847f6112bb..fe689b105fa 100644 --- a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h +++ b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h @@ -8,6 +8,7 @@ #include "YogaLayoutableShadowNode.g.h" #include #include +#include "AbiEventEmitter.h" #include "AbiState.h" #include "AbiViewProps.h" @@ -42,6 +43,8 @@ struct ShadowNode : ShadowNodeT { winrt::IInspectable StateData() const noexcept; void StateData(winrt::IInspectable tag) noexcept; + winrt::Microsoft::ReactNative::EventEmitter EventEmitter() const noexcept; + protected: facebook::react::ShadowNode::Weak m_shadowNode; winrt::IInspectable m_tag; diff --git a/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp index a8ef1810dbf..e6c885b9cb8 100644 --- a/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp @@ -12,6 +12,7 @@ #include "MountChildComponentViewArgs.g.cpp" #include "UnmountChildComponentViewArgs.g.cpp" #include +#include "AbiEventEmitter.h" #include "AbiShadowNode.h" namespace winrt::Microsoft::ReactNative::Composition::implementation { @@ -58,14 +59,10 @@ void ComponentView::MountChildComponentView( } } -void ComponentView::MountChildComponentViewHandler(const MountChildComponentViewDelegate &handler) { +void ComponentView::MountChildComponentViewHandler(const MountChildComponentViewDelegate &handler) noexcept { m_mountChildComponentViewHandler = handler; } -MountChildComponentViewDelegate ComponentView::MountChildComponentViewHandler() const noexcept { - return m_mountChildComponentViewHandler; -} - void ComponentView::onMounted() noexcept { assert(!m_mounted); m_mounted = true; @@ -94,14 +91,10 @@ void ComponentView::UnmountChildComponentView( winrt::get_self(childComponentView)->parent(nullptr); winrt::get_self(childComponentView)->onUnmounted(); } -void ComponentView::UnmountChildComponentViewHandler(const UnmountChildComponentViewDelegate &handler) { +void ComponentView::UnmountChildComponentViewHandler(const UnmountChildComponentViewDelegate &handler) noexcept { m_unmountChildComponentViewHandler = handler; } -UnmountChildComponentViewDelegate ComponentView::UnmountChildComponentViewHandler() const noexcept { - return m_unmountChildComponentViewHandler; -} - void ComponentView::onUnmounted() noexcept { assert(m_mounted); m_mounted = false; @@ -154,14 +147,10 @@ void ComponentView::updateProps( } } -void ComponentView::UpdatePropsHandler(const UpdatePropsDelegate &handler) { +void ComponentView::UpdatePropsHandler(const UpdatePropsDelegate &handler) noexcept { m_updatePropsDelegate = handler; } -UpdatePropsDelegate ComponentView::UpdatePropsHandler() const noexcept { - return m_updatePropsDelegate; -} - const winrt::Microsoft::ReactNative::IComponentProps ComponentView::userProps( facebook::react::Props::Shared const &props) noexcept { const auto &abiProps = @@ -169,7 +158,15 @@ const winrt::Microsoft::ReactNative::IComponentProps ComponentView::userProps( return abiProps.UserProps(); } -void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept {} +void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept { + if (m_updateEventEmitterHandler) { + m_updateEventEmitterHandler(*this, winrt::make(eventEmitter)); + } +} + +void ComponentView::UpdateEventEmitterHandler(const UpdateEventEmitterDelegate &handler) noexcept { + m_updateEventEmitterHandler = handler; +} void ComponentView::updateState( facebook::react::State::Shared const &state, @@ -180,14 +177,10 @@ void ComponentView::updateState( } } -void ComponentView::UpdateStateHandler(const UpdateStateDelegate &handler) { +void ComponentView::UpdateStateHandler(const UpdateStateDelegate &handler) noexcept { m_updateStateDelegate = handler; } -UpdateStateDelegate ComponentView::UpdateStateHandler() const noexcept { - return m_updateStateDelegate; -} - LayoutMetricsChangedArgs::LayoutMetricsChangedArgs( const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics, const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) @@ -241,14 +234,10 @@ void ComponentView::LayoutMetricsChanged(winrt::event_token const &token) noexce m_layoutMetricsChangedEvent.remove(token); } -void ComponentView::FinalizeUpdateHandler(const UpdateFinalizerDelegate &handler) { +void ComponentView::FinalizeUpdateHandler(const UpdateFinalizerDelegate &handler) noexcept { m_finalizeUpdateHandler = handler; } -UpdateFinalizerDelegate ComponentView::FinalizeUpdateHandler() const noexcept { - return m_finalizeUpdateHandler; -} - void ComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept { if (m_finalizeUpdateHandler) { m_finalizeUpdateHandler(*this, updateMask); @@ -262,14 +251,10 @@ facebook::react::Props::Shared ComponentView::props() noexcept { return {}; } -void ComponentView::CustomCommandHandler(const HandleCommandDelegate &handler) { +void ComponentView::CustomCommandHandler(const HandleCommandDelegate &handler) noexcept { m_customCommandHandler = handler; } -HandleCommandDelegate ComponentView::CustomCommandHandler() const noexcept { - return m_customCommandHandler; -} - void ComponentView::HandleCommand( winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/ComponentView.h b/vnext/Microsoft.ReactNative/Fabric/ComponentView.h index 79fa4ec4a15..aaea68fa5ef 100644 --- a/vnext/Microsoft.ReactNative/Fabric/ComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/ComponentView.h @@ -211,23 +211,13 @@ struct ComponentView : public ComponentViewT { void UserData(const winrt::IInspectable &userData) noexcept; winrt::IInspectable UserData() const noexcept; - void CustomCommandHandler(const HandleCommandDelegate &handler); - HandleCommandDelegate CustomCommandHandler() const noexcept; - - void UpdatePropsHandler(const UpdatePropsDelegate &handler); - UpdatePropsDelegate UpdatePropsHandler() const noexcept; - - void UpdateStateHandler(const UpdateStateDelegate &handler); - UpdateStateDelegate UpdateStateHandler() const noexcept; - - void MountChildComponentViewHandler(const MountChildComponentViewDelegate &handler); - MountChildComponentViewDelegate MountChildComponentViewHandler() const noexcept; - - void UnmountChildComponentViewHandler(const UnmountChildComponentViewDelegate &handler); - UnmountChildComponentViewDelegate UnmountChildComponentViewHandler() const noexcept; - - void FinalizeUpdateHandler(const UpdateFinalizerDelegate &handler); - UpdateFinalizerDelegate FinalizeUpdateHandler() const noexcept; + void CustomCommandHandler(const HandleCommandDelegate &handler) noexcept; + void UpdatePropsHandler(const UpdatePropsDelegate &handler) noexcept; + void UpdateStateHandler(const UpdateStateDelegate &handler) noexcept; + void UpdateEventEmitterHandler(const UpdateEventEmitterDelegate &handler) noexcept; + void MountChildComponentViewHandler(const MountChildComponentViewDelegate &handler) noexcept; + void UnmountChildComponentViewHandler(const UnmountChildComponentViewDelegate &handler) noexcept; + void FinalizeUpdateHandler(const UpdateFinalizerDelegate &handler) noexcept; virtual void MountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, @@ -276,6 +266,7 @@ struct ComponentView : public ComponentViewT { UpdateFinalizerDelegate m_finalizeUpdateHandler{nullptr}; MountChildComponentViewDelegate m_mountChildComponentViewHandler{nullptr}; UnmountChildComponentViewDelegate m_unmountChildComponentViewHandler{nullptr}; + UpdateEventEmitterDelegate m_updateEventEmitterHandler{nullptr}; winrt::event< winrt::Windows::Foundation::EventHandler> diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index 6f131e41296..a196b0f2717 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -245,6 +245,7 @@ void ComponentView::StartBringIntoView( void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept { m_eventEmitter = std::static_pointer_cast(eventEmitter); + base_type::updateEventEmitter(eventEmitter); } void ComponentView::HandleCommand( diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp index a54c8109d7c..0f90896ccd8 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp @@ -67,10 +67,6 @@ void ParagraphComponentView::updateProps( Super::updateProps(props, oldProps); } -void ParagraphComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept { - Super::updateEventEmitter(eventEmitter); -} - void ParagraphComponentView::updateState( facebook::react::State::Shared const &state, facebook::react::State::Shared const &oldState) noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h index 4e74704fa32..648b6e41a04 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h @@ -33,7 +33,6 @@ struct ParagraphComponentView : ParagraphComponentViewTUpdatePropsHandler(m_updatePropsHandler); if (m_updateStateHandler) self->UpdateStateHandler(m_updateStateHandler); + if (m_updateEventEmitterHandler) + self->UpdateEventEmitterHandler(m_updateEventEmitterHandler); if (m_mountChildComponentViewHandler) self->MountChildComponentViewHandler(m_mountChildComponentViewHandler); if (m_unmountChildComponentViewHandler) @@ -166,6 +168,10 @@ void ReactCompositionViewComponentBuilder::SetUpdateStateHandler(UpdateStateDele m_updateStateHandler = impl; } +void ReactCompositionViewComponentBuilder::SetUpdateEventEmitterHandler(UpdateEventEmitterDelegate impl) noexcept { + m_updateEventEmitterHandler = impl; +} + void ReactCompositionViewComponentBuilder::SetMountChildComponentViewHandler( MountChildComponentViewDelegate impl) noexcept { m_mountChildComponentViewHandler = impl; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h index 9618c203c50..14e1a21ff11 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h @@ -35,6 +35,7 @@ struct ReactCompositionViewComponentBuilder : winrt::implements< void SetFinalizeUpdateHandler(UpdateFinalizerDelegate impl) noexcept; void SetUpdatePropsHandler(UpdatePropsDelegate impl) noexcept; void SetUpdateStateHandler(UpdateStateDelegate impl) noexcept; + void SetUpdateEventEmitterHandler(UpdateEventEmitterDelegate impl) noexcept; void SetMountChildComponentViewHandler(MountChildComponentViewDelegate impl) noexcept; void SetUnmountChildComponentViewHandler(UnmountChildComponentViewDelegate impl) noexcept; @@ -78,6 +79,7 @@ struct ReactCompositionViewComponentBuilder : winrt::implements< winrt::Microsoft::ReactNative::UpdateFinalizerDelegate m_finalizeUpdateHandler; winrt::Microsoft::ReactNative::UpdatePropsDelegate m_updatePropsHandler; winrt::Microsoft::ReactNative::UpdateStateDelegate m_updateStateHandler; + winrt::Microsoft::ReactNative::UpdateEventEmitterDelegate m_updateEventEmitterHandler; winrt::Microsoft::ReactNative::MountChildComponentViewDelegate m_mountChildComponentViewHandler; winrt::Microsoft::ReactNative::UnmountChildComponentViewDelegate m_unmountChildComponentViewHandler; diff --git a/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl b/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl index c414d7f681b..33617e4eec5 100644 --- a/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl +++ b/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl @@ -3,6 +3,7 @@ import "ViewProps.idl"; import "ComponentView.idl"; +import "IJSValueWriter.idl"; #include "DocString.h" @@ -87,6 +88,14 @@ namespace Microsoft.ReactNative [experimental] delegate void UnmountChildComponentViewDelegate(ComponentView source, UnmountChildComponentViewArgs args); + [experimental] + runtimeclass EventEmitter { + void DispatchEvent(String eventName, JSValueArgWriter args); + }; + + [experimental] + delegate void UpdateEventEmitterDelegate(ComponentView source, EventEmitter eventEmitter); + [webhosthidden] [experimental] interface IReactViewComponentBuilder @@ -105,6 +114,7 @@ namespace Microsoft.ReactNative void SetFinalizeUpdateHandler(UpdateFinalizerDelegate impl); void SetUpdatePropsHandler(UpdatePropsDelegate impl); void SetUpdateStateHandler(UpdateStateDelegate impl); + void SetUpdateEventEmitterHandler(UpdateEventEmitterDelegate impl); void SetMountChildComponentViewHandler(MountChildComponentViewDelegate impl); void SetUnmountChildComponentViewHandler(UnmountChildComponentViewDelegate impl); }; @@ -123,6 +133,7 @@ namespace Microsoft.ReactNative void EnsureUnsealed(); Object Tag { get; set; }; Object StateData{ get; set; }; + EventEmitter EventEmitter { get; }; }; [webhosthidden] diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 1f354828aad..f028abf21e8 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -159,6 +159,9 @@ true + + true + true