react-native-windows 0.74.33 → 0.74.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/Libraries/Modal/Modal.windows.js +4 -1
  2. package/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.cpp +97 -0
  3. package/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.h +53 -0
  4. package/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h +161 -17
  5. package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +18 -0
  6. package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h +10 -0
  7. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +74 -42
  8. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +2 -0
  9. package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +152 -1
  10. package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +16 -0
  11. package/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp +4 -2
  12. package/Microsoft.ReactNative/Fabric/Composition/FocusManager.h +9 -1
  13. package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +179 -12
  14. package/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.cpp +12 -0
  15. package/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp +2 -1
  16. package/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h +0 -1
  17. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +16 -14
  18. package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +6 -4
  19. package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +3 -1
  20. package/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +5 -0
  21. package/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/PlatformColorParser.h +1 -1
  22. package/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl +10 -1
  23. package/Microsoft.ReactNative/Modules/ClipboardModule.cpp +1 -1
  24. package/Microsoft.ReactNative/Modules/ClipboardModule.h +1 -1
  25. package/Microsoft.ReactNative/packages.lock.json +103 -27
  26. package/Microsoft.ReactNative.Cxx/NativeModules.h +20 -0
  27. package/PropertySheets/Generated/PackageVersion.g.props +3 -3
  28. package/PropertySheets/WinUI.props +1 -1
  29. package/Shared/Shared.vcxitems +3 -3
  30. package/Shared/Shared.vcxitems.filters +1 -1
  31. package/codegen/react/components/rnwcore/ActivityIndicatorView.g.h +1 -1
  32. package/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h +1 -1
  33. package/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h +1 -1
  34. package/codegen/react/components/rnwcore/AndroidProgressBar.g.h +1 -1
  35. package/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h +1 -1
  36. package/codegen/react/components/rnwcore/AndroidSwitch.g.h +1 -1
  37. package/codegen/react/components/rnwcore/DebuggingOverlay.g.h +1 -1
  38. package/codegen/react/components/rnwcore/InputAccessory.g.h +1 -1
  39. package/codegen/react/components/rnwcore/ModalHostView.g.h +1 -1
  40. package/codegen/react/components/rnwcore/PullToRefreshView.g.h +1 -1
  41. package/codegen/react/components/rnwcore/SafeAreaView.g.h +1 -1
  42. package/codegen/react/components/rnwcore/Switch.g.h +1 -1
  43. package/codegen/react/components/rnwcore/UnimplementedNativeView.g.h +1 -1
  44. package/package.json +3 -3
  45. package/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.cpp +0 -191
@@ -274,8 +274,11 @@ class Modal extends React.Component<Props, State> {
274
274
  }
275
275
  }
276
276
 
277
+ // [Windows] - apply empty rootViewStyle to AppContainer to prevent modal from always expanding to fill available space
277
278
  const innerChildren = __DEV__ ? (
278
- <AppContainer rootTag={this.context}>{this.props.children}</AppContainer>
279
+ <AppContainer rootTag={this.context} rootViewStyle={{}}>
280
+ {this.props.children}
281
+ </AppContainer>
279
282
  ) : (
280
283
  this.props.children
281
284
  );
@@ -0,0 +1,97 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "AbiPortalShadowNode.h"
5
+
6
+ #include <Fabric/Composition/ReactCompositionViewComponentBuilder.h>
7
+ #include <react/debug/react_native_assert.h>
8
+ #include <react/renderer/core/LayoutConstraints.h>
9
+ #include <react/renderer/core/LayoutContext.h>
10
+ #include <react/renderer/core/conversions.h>
11
+
12
+ #include <utility>
13
+
14
+ namespace Microsoft::ReactNative {
15
+
16
+ extern const char AbiPortalComponentName[] = "AbiPortal";
17
+
18
+ facebook::react::Size AbiPortalShadowNode::measureContent(
19
+ const facebook::react::LayoutContext &layoutContext,
20
+ const facebook::react::LayoutConstraints &layoutConstraints) const {
21
+ return {0, 0}; // The portal placeholder node shouldn't take up any space
22
+ }
23
+
24
+ void AbiPortalShadowNode::layout(facebook::react::LayoutContext layoutContext) {
25
+ ensureUnsealed();
26
+ auto layoutMetrics = getLayoutMetrics();
27
+
28
+ auto portalOwningShadowNode = ShadowNode::Unshared{};
29
+
30
+ if (getChildren().empty()) {
31
+ return;
32
+ }
33
+
34
+ // A Portal should only have a single child
35
+ react_native_assert(getChildren().size() == 1);
36
+
37
+ const auto &childNode = getChildren()[0];
38
+
39
+ auto clonedShadowNode = ShadowNode::Unshared{};
40
+
41
+ portalOwningShadowNode = cloneTree(childNode->getFamily(), [&](const ShadowNode &oldShadowNode) {
42
+ clonedShadowNode = oldShadowNode.clone({});
43
+ return clonedShadowNode;
44
+ });
45
+ auto portalShadowNode = static_cast<AbiPortalShadowNode *>(portalOwningShadowNode.get());
46
+
47
+ auto &layoutableShadowNode = dynamic_cast<LayoutableShadowNode &>(*clonedShadowNode);
48
+
49
+ auto &state = getStateData();
50
+
51
+ facebook::react::LayoutConstraints layoutConstraints;
52
+ layoutConstraints.layoutDirection = layoutMetrics.layoutDirection;
53
+
54
+ if (state.userdata) {
55
+ // If the portal component set a state of type IPortalStateData,
56
+ // extract constraint information from it, and use that for layout
57
+ if (auto portalState = state.userdata.try_as<winrt::Microsoft::ReactNative::Composition::IPortalStateData>()) {
58
+ auto stateConstraints = portalState.LayoutConstraints();
59
+
60
+ layoutConstraints.minimumSize = {stateConstraints.MinimumSize.Width, stateConstraints.MinimumSize.Height};
61
+ layoutConstraints.maximumSize = {stateConstraints.MaximumSize.Width, stateConstraints.MaximumSize.Height};
62
+ if (stateConstraints.LayoutDirection == winrt::Microsoft::ReactNative::LayoutDirection::LeftToRight) {
63
+ layoutConstraints.layoutDirection = facebook::react::LayoutDirection::LeftToRight;
64
+ } else if (stateConstraints.LayoutDirection == winrt::Microsoft::ReactNative::LayoutDirection::RightToLeft) {
65
+ layoutConstraints.layoutDirection = facebook::react::LayoutDirection::RightToLeft;
66
+ }
67
+ }
68
+ }
69
+
70
+ // Laying out the `ShadowNode` and the subtree starting from it.
71
+ layoutableShadowNode.layoutTree(layoutContext, layoutConstraints);
72
+
73
+ auto childLayoutMetrics = layoutableShadowNode.getLayoutMetrics();
74
+ childLayoutMetrics.frame.origin = {0, 0};
75
+ layoutableShadowNode.setLayoutMetrics(childLayoutMetrics);
76
+
77
+ // Update the list of children to reflect the changes that we made.
78
+ this->children_ = static_cast<AbiPortalShadowNode *>(portalOwningShadowNode.get())->children_;
79
+ }
80
+
81
+ void AbiPortalShadowNode::Builder(winrt::Microsoft::ReactNative::IReactViewComponentBuilder builder) noexcept {
82
+ m_builder = builder;
83
+ }
84
+
85
+ winrt::Microsoft::ReactNative::IReactViewComponentBuilder AbiPortalShadowNode::Builder() const noexcept {
86
+ return m_builder;
87
+ }
88
+
89
+ void AbiPortalShadowNode::Proxy(winrt::Microsoft::ReactNative::ShadowNode proxy) noexcept {
90
+ m_proxy = proxy;
91
+ }
92
+
93
+ winrt::Microsoft::ReactNative::ShadowNode AbiPortalShadowNode::Proxy() const noexcept {
94
+ return m_proxy;
95
+ }
96
+
97
+ } // namespace Microsoft::ReactNative
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #pragma once
5
+
6
+ #include <react/components/rnwcore/EventEmitters.h>
7
+ #include <unordered_map>
8
+ #include "AbiShadowNode.h"
9
+ #include "AbiState.h"
10
+ #include "AbiViewProps.h"
11
+
12
+ #include <react/renderer/components/view/ConcreteViewShadowNode.h>
13
+ #include <react/renderer/core/LayoutContext.h>
14
+
15
+ namespace Microsoft::ReactNative {
16
+
17
+ extern const char AbiPortalComponentName[];
18
+
19
+ class AbiPortalShadowNode final : public facebook::react::ConcreteViewShadowNode<
20
+ AbiPortalComponentName,
21
+ AbiViewProps,
22
+ facebook::react::ViewEventEmitter,
23
+ Microsoft::ReactNative::AbiStateData> {
24
+ public:
25
+ using ConcreteViewShadowNode::ConcreteViewShadowNode;
26
+
27
+ static facebook::react::ShadowNodeTraits BaseTraits() {
28
+ auto traits = facebook::react::ShadowNode::BaseTraits();
29
+ traits.set(facebook::react::ShadowNodeTraits::Trait::FormsStackingContext);
30
+ traits.set(facebook::react::ShadowNodeTraits::Trait::FormsView);
31
+ traits.set(facebook::react::ShadowNodeTraits::Trait::RootNodeKind);
32
+ traits.set(facebook::react::ShadowNodeTraits::Trait::LeafYogaNode);
33
+ traits.set(facebook::react::ShadowNodeTraits::Trait::MeasurableYogaNode);
34
+ return traits;
35
+ }
36
+
37
+ facebook::react::Size measureContent(
38
+ const facebook::react::LayoutContext &layoutContext,
39
+ const facebook::react::LayoutConstraints &layoutConstraints) const override;
40
+ void layout(facebook::react::LayoutContext layoutContext) override;
41
+
42
+ void OnClone(const facebook::react::ShadowNode &sourceShadowNode) noexcept;
43
+ void Builder(winrt::Microsoft::ReactNative::IReactViewComponentBuilder builder) noexcept;
44
+ winrt::Microsoft::ReactNative::IReactViewComponentBuilder Builder() const noexcept;
45
+ void Proxy(winrt::Microsoft::ReactNative::ShadowNode handle) noexcept;
46
+ winrt::Microsoft::ReactNative::ShadowNode Proxy() const noexcept;
47
+
48
+ private:
49
+ winrt::Microsoft::ReactNative::ShadowNode m_proxy{nullptr};
50
+ winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder{nullptr};
51
+ };
52
+
53
+ } // namespace Microsoft::ReactNative
@@ -3,16 +3,22 @@
3
3
 
4
4
  #pragma once
5
5
 
6
+ #include <Fabric/Composition/ReactCompositionViewComponentBuilder.h>
7
+ #include <Fabric/WindowsComponentDescriptorRegistry.h>
8
+ #include <ReactContext.h>
6
9
  #include <react/renderer/components/view/ConcreteViewShadowNode.h>
7
10
  #include <react/renderer/core/ComponentDescriptor.h>
11
+ #include "AbiPortalShadowNode.h"
8
12
  #include "AbiViewProps.h"
9
13
  #include "AbiViewShadowNode.h"
14
+ #include "DynamicReader.h"
10
15
  #include "winrt/Microsoft.ReactNative.h"
11
16
 
12
17
  namespace Microsoft::ReactNative {
13
18
 
14
- class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor {
15
- using ShadowNodeT = AbiViewShadowNode;
19
+ template <typename ShadowNodeT>
20
+ class ConcreteAbiViewComponentDescriptor : public facebook::react::ComponentDescriptor {
21
+ protected:
16
22
  using SharedShadowNodeT = std::shared_ptr<const ShadowNodeT>;
17
23
 
18
24
  public:
@@ -24,34 +30,143 @@ class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor {
24
30
  using ConcreteState = typename ShadowNodeT::ConcreteState;
25
31
  using ConcreteStateData = typename ShadowNodeT::ConcreteState::Data;
26
32
 
27
- AbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const &parameters);
33
+ ConcreteAbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const &parameters)
34
+ : ComponentDescriptor(parameters) {
35
+ auto flavor = std::static_pointer_cast<std::string const>(this->flavor_);
36
+ m_builder = WindowsComponentDescriptorRegistry::FromProperties(
37
+ parameters.contextContainer->at<winrt::Microsoft::ReactNative::ReactContext>("MSRN.ReactContext")
38
+ .Properties())
39
+ ->GetDescriptor(flavor);
40
+
41
+ rawPropsParser_.prepare<ConcreteProps>();
42
+ }
43
+ facebook::react::ComponentHandle getComponentHandle() const override {
44
+ return reinterpret_cast<facebook::react::ComponentHandle>(getComponentName());
45
+ }
46
+
47
+ facebook::react::ComponentName getComponentName() const override {
48
+ return std::static_pointer_cast<std::string const>(this->flavor_)->c_str();
49
+ }
50
+
51
+ facebook::react::ShadowNodeTraits getTraits() const override {
52
+ auto traits = ShadowNodeT::BaseTraits();
53
+ if (winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
54
+ ->MeasureContentHandler()) {
55
+ traits.set(facebook::react::ShadowNodeTraits::LeafYogaNode);
56
+ traits.set(facebook::react::ShadowNodeTraits::MeasurableYogaNode);
57
+ }
58
+ return traits;
59
+ }
28
60
 
29
- facebook::react::ComponentHandle getComponentHandle() const override;
30
- facebook::react::ComponentName getComponentName() const override;
31
- facebook::react::ShadowNodeTraits getTraits() const override;
32
61
  std::shared_ptr<facebook::react::ShadowNode> createShadowNode(
33
62
  const facebook::react::ShadowNodeFragment &fragment,
34
- facebook::react::ShadowNodeFamily::Shared const &family) const override;
63
+ facebook::react::ShadowNodeFamily::Shared const &family) const override {
64
+ auto shadowNode = std::make_shared<ShadowNodeT>(fragment, family, getTraits());
65
+ shadowNode->Proxy(winrt::make<winrt::Microsoft::ReactNative::implementation::YogaLayoutableShadowNode>(shadowNode));
66
+ winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
67
+ ->CreateShadowNode(shadowNode->Proxy());
68
+
69
+ adopt(*shadowNode);
70
+ return shadowNode;
71
+ }
72
+
35
73
  facebook::react::ShadowNode::Unshared cloneShadowNode(
36
74
  const facebook::react::ShadowNode &sourceShadowNode,
37
- const facebook::react::ShadowNodeFragment &fragment) const override;
75
+ const facebook::react::ShadowNodeFragment &fragment) const override {
76
+ auto shadowNode = std::make_shared<ShadowNodeT>(sourceShadowNode, fragment);
77
+ shadowNode->Proxy(winrt::make<winrt::Microsoft::ReactNative::implementation::YogaLayoutableShadowNode>(shadowNode));
78
+ winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
79
+ ->CloneShadowNode(shadowNode->Proxy(), static_cast<const ShadowNodeT &>(sourceShadowNode).Proxy());
80
+
81
+ adopt(*shadowNode);
82
+ return shadowNode;
83
+ }
38
84
 
39
85
  void appendChild(
40
86
  const facebook::react::ShadowNode::Shared &parentShadowNode,
41
- const facebook::react::ShadowNode::Shared &childShadowNode) const override;
87
+ const facebook::react::ShadowNode::Shared &childShadowNode) const override {
88
+ auto concreteParentShadowNode = std::static_pointer_cast<const ShadowNodeT>(parentShadowNode);
89
+ auto concreteNonConstParentShadowNode = std::const_pointer_cast<ShadowNodeT>(concreteParentShadowNode);
90
+ concreteNonConstParentShadowNode->appendChild(childShadowNode);
91
+ }
92
+
42
93
  virtual facebook::react::Props::Shared cloneProps(
43
94
  const facebook::react::PropsParserContext &context,
44
95
  const facebook::react::Props::Shared &props,
45
- facebook::react::RawProps rawProps) const override;
96
+ facebook::react::RawProps rawProps) const override {
97
+ // Optimization:
98
+ // Quite often nodes are constructed with default/empty props: the base
99
+ // `props` object is `null` (there no base because it's not cloning) and the
100
+ // `rawProps` is empty. In this case, we can return the default props object
101
+ // of a concrete type entirely bypassing parsing.
102
+ if (!props && rawProps.isEmpty()) {
103
+ return ShadowNodeT::defaultSharedProps();
104
+ }
105
+
106
+ if constexpr (facebook::react::RawPropsFilterable<ShadowNodeT>) {
107
+ ShadowNodeT::filterRawProps(rawProps);
108
+ }
109
+
110
+ rawProps.parse(rawPropsParser_);
111
+
112
+ // Call old-style constructor
113
+ // auto shadowNodeProps = std::make_shared<ShadowNodeT::Props>(context, rawProps, props);
114
+ auto shadowNodeProps = std::make_shared<AbiViewProps>(
115
+ context, props ? static_cast<AbiViewProps const &>(*props) : *ShadowNodeT::defaultSharedProps(), rawProps);
116
+ auto viewProps =
117
+ winrt::make<winrt::Microsoft::ReactNative::implementation::ViewProps>(shadowNodeProps, false /*holdRef*/);
118
+ auto userProps =
119
+ winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
120
+ ->CreateProps(viewProps, props ? static_cast<AbiViewProps const &>(*props).UserProps() : nullptr);
121
+ shadowNodeProps->SetUserProps(userProps, viewProps);
122
+
123
+ rawProps.iterateOverValues(
124
+ [&](facebook::react::RawPropsPropNameHash hash, const char *propName, facebook::react::RawValue const &fn) {
125
+ shadowNodeProps.get()->setProp(context, hash, propName, fn);
126
+ userProps.SetProp(
127
+ hash,
128
+ winrt::to_hstring(propName),
129
+ winrt::make<winrt::Microsoft::ReactNative::DynamicReader>(folly::dynamic(fn)));
130
+ });
131
+
132
+ return shadowNodeProps;
133
+ }
134
+
46
135
  virtual facebook::react::State::Shared createInitialState(
47
136
  facebook::react::Props::Shared const &props,
48
- facebook::react::ShadowNodeFamily::Shared const &family) const override;
137
+ facebook::react::ShadowNodeFamily::Shared const &family) const override {
138
+ if (std::is_same<ConcreteStateData, facebook::react::StateData>::value) {
139
+ // Default case: Returning `null` for nodes that don't use `State`.
140
+ return nullptr;
141
+ }
142
+
143
+ return std::make_shared<ConcreteState>(
144
+ std::make_shared<ConcreteStateData const>(
145
+ ConcreteAbiViewComponentDescriptor<ShadowNodeT>::initialStateData(props, family, *this)),
146
+ family);
147
+ }
148
+
49
149
  virtual facebook::react::State::Shared createState(
50
150
  facebook::react::ShadowNodeFamily const &family,
51
- facebook::react::StateData::Shared const &data) const override;
151
+ facebook::react::StateData::Shared const &data) const override {
152
+ if (std::is_same<ConcreteStateData, facebook::react::StateData>::value) {
153
+ // Default case: Returning `null` for nodes that don't use `State`.
154
+ return nullptr;
155
+ }
156
+
157
+ react_native_assert(data && "Provided `data` is nullptr.");
158
+
159
+ return std::make_shared<ConcreteState const>(
160
+ std::static_pointer_cast<ConcreteStateData const>(data), *family.getMostRecentState());
161
+ }
52
162
 
53
163
  facebook::react::ShadowNodeFamily::Shared createFamily(
54
- facebook::react::ShadowNodeFamilyFragment const &fragment) const override;
164
+ facebook::react::ShadowNodeFamilyFragment const &fragment) const override {
165
+ auto eventEmitter = std::make_shared<const ConcreteEventEmitter>(
166
+ std::make_shared<facebook::react::EventTarget>(fragment.instanceHandle), eventDispatcher_);
167
+ return std::make_shared<facebook::react::ShadowNodeFamily>(
168
+ fragment, std::move(eventEmitter), eventDispatcher_, *this);
169
+ }
55
170
 
56
171
  protected:
57
172
  /*
@@ -66,15 +181,44 @@ class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor {
66
181
  * - Set `ShadowNode`'s size from state in
67
182
  * `ModalHostViewComponentDescriptor`.
68
183
  */
69
- virtual void adopt(facebook::react::ShadowNode &shadowNode) const;
184
+ virtual void adopt(facebook::react::ShadowNode &shadowNode) const {
185
+ react_native_assert(shadowNode.getComponentHandle() == getComponentHandle());
186
+
187
+ auto &abiViewShadowNode = static_cast<AbiViewShadowNode &>(shadowNode);
188
+
189
+ abiViewShadowNode.Builder(m_builder);
190
+
191
+ if (winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
192
+ ->MeasureContentHandler()) {
193
+ abiViewShadowNode.dirtyLayout();
194
+ abiViewShadowNode.enableMeasurement();
195
+ }
196
+ }
70
197
 
71
198
  private:
72
199
  static ConcreteStateData initialStateData(
73
- const facebook::react::Props::Shared & /*props*/,
200
+ const facebook::react::Props::Shared &props,
74
201
  const facebook::react::ShadowNodeFamily::Shared & /*family*/,
75
- const facebook::react::ComponentDescriptor & /*componentDescriptor*/) noexcept;
202
+ const facebook::react::ComponentDescriptor &componentDescriptor) noexcept {
203
+ return {winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(
204
+ static_cast<const ConcreteAbiViewComponentDescriptor<ShadowNodeT> &>(componentDescriptor).m_builder)
205
+ ->InitialStateData(std::static_pointer_cast<AbiViewProps const>(props)->UserProps())};
206
+ return {};
207
+ }
208
+
209
+ winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder{nullptr};
210
+ };
76
211
 
77
- winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder;
212
+ class AbiViewComponentDescriptor : public ConcreteAbiViewComponentDescriptor<AbiViewShadowNode> {
213
+ public:
214
+ AbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const &parameters)
215
+ : ConcreteAbiViewComponentDescriptor<AbiViewShadowNode>(parameters) {}
216
+ };
217
+
218
+ class AbiPortalComponentDescriptor : public ConcreteAbiViewComponentDescriptor<AbiPortalShadowNode> {
219
+ public:
220
+ AbiPortalComponentDescriptor(facebook::react::ComponentDescriptorParameters const &parameters)
221
+ : ConcreteAbiViewComponentDescriptor<AbiPortalShadowNode>(parameters) {}
78
222
  };
79
223
 
80
224
  } // namespace Microsoft::ReactNative
@@ -4,6 +4,7 @@
4
4
  #include <Fabric/Composition/SwitchComponentView.h>
5
5
  #include <Fabric/Composition/TextInput/WindowsTextInputComponentView.h>
6
6
  #include <Unicode.h>
7
+ #include <winrt/Microsoft.UI.Content.h>
7
8
  #include "RootComponentView.h"
8
9
  #include "UiaHelpers.h"
9
10
 
@@ -27,12 +28,29 @@ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
27
28
  }
28
29
  }
29
30
 
31
+ #ifdef USE_EXPERIMENTAL_WINUI3
32
+ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
33
+ const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView,
34
+ const winrt::Microsoft::UI::Content::ChildSiteLink &childSiteLink) noexcept
35
+ : m_view{componentView}, m_childSiteLink{childSiteLink} {}
36
+ #endif // USE_EXPERIMENTAL_WINUI3
37
+
30
38
  HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
31
39
  NavigateDirection direction,
32
40
  IRawElementProviderFragment **pRetVal) {
33
41
  if (pRetVal == nullptr)
34
42
  return E_POINTER;
35
43
 
44
+ #ifdef USE_EXPERIMENTAL_WINUI3
45
+ if (m_childSiteLink) {
46
+ if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) {
47
+ auto fragment = m_childSiteLink.AutomationProvider().try_as<IRawElementProviderFragment>();
48
+ *pRetVal = fragment.detach();
49
+ return S_OK;
50
+ }
51
+ }
52
+ #endif // USE_EXPERIMENTAL_WINUI3
53
+
36
54
  return UiaNavigateHelper(m_view.view(), direction, *pRetVal);
37
55
  }
38
56
 
@@ -25,6 +25,12 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
25
25
  CompositionDynamicAutomationProvider(
26
26
  const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView) noexcept;
27
27
 
28
+ #ifdef USE_EXPERIMENTAL_WINUI3
29
+ CompositionDynamicAutomationProvider(
30
+ const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView,
31
+ const winrt::Microsoft::UI::Content::ChildSiteLink &childContentLink) noexcept;
32
+ #endif // USE_EXPERIMENTAL_WINUI3
33
+
28
34
  // inherited via IRawElementProviderFragment
29
35
  virtual HRESULT __stdcall Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal) override;
30
36
  virtual HRESULT __stdcall GetRuntimeId(SAFEARRAY **pRetVal) override;
@@ -86,6 +92,10 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
86
92
  private:
87
93
  ::Microsoft::ReactNative::ReactTaggedView m_view;
88
94
  std::vector<winrt::com_ptr<IRawElementProviderSimple>> m_selectionItems;
95
+ #ifdef USE_EXPERIMENTAL_WINUI3
96
+ // Non-null when this UIA node is the peer of a ContentIslandComponentView.
97
+ winrt::Microsoft::UI::Content::ChildSiteLink m_childSiteLink{nullptr};
98
+ #endif
89
99
  };
90
100
 
91
101
  } // namespace winrt::Microsoft::ReactNative::implementation
@@ -33,6 +33,7 @@
33
33
  namespace winrt::Microsoft::ReactNative::Composition::implementation {
34
34
 
35
35
  constexpr float FOCUS_VISUAL_WIDTH = 2.0f;
36
+ constexpr float FOCUS_VISUAL_RADIUS = 3.0f;
36
37
 
37
38
  // m_outerVisual
38
39
  // |
@@ -168,11 +169,14 @@ void ComponentView::updateProps(
168
169
  m_componentHostingFocusVisual->hostFocusVisual(false, get_strong());
169
170
  }
170
171
 
171
- if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) {
172
- m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps);
173
- }
174
- if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) {
175
- m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps);
172
+ // We have to check m_componentHostingFocusVisual again, as it can be set to null by above hostFocusVisual call
173
+ if (m_componentHostingFocusVisual) {
174
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) {
175
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps);
176
+ }
177
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) {
178
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps);
179
+ }
176
180
  }
177
181
  }
178
182
  if ((m_flags & ComponentViewFeatures::ShadowProps) == ComponentViewFeatures::ShadowProps) {
@@ -225,29 +229,11 @@ void ComponentView::updateFocusLayoutMetrics() noexcept {
225
229
  facebook::react::RectangleEdges<bool> nudgeEdges;
226
230
  auto scaleFactor = m_focusPrimitive->m_focusVisualComponent->m_layoutMetrics.pointScaleFactor;
227
231
  if (m_focusPrimitive) {
232
+ auto nudgeEdges = m_focusPrimitive->m_focusVisualComponent->focusNudges();
228
233
  if (m_focusPrimitive->m_focusOuterPrimitive) {
229
234
  auto outerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(false /*inner*/);
230
-
231
- if (outerFocusMetrics.frame.origin.x < 0) {
232
- nudgeEdges.left = true;
233
- }
234
- if (outerFocusMetrics.frame.origin.y < 0) {
235
- nudgeEdges.top = true;
236
- }
237
- if (outerFocusMetrics.frame.getMaxX() > m_layoutMetrics.frame.getMaxX()) {
238
- nudgeEdges.right = true;
239
- }
240
- if (outerFocusMetrics.frame.getMaxY() > m_layoutMetrics.frame.getMaxY()) {
241
- nudgeEdges.bottom = true;
242
- }
243
-
244
235
  m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Size(
245
- {outerFocusMetrics.frame.size.width * scaleFactor -
246
- (nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) -
247
- (nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0),
248
- outerFocusMetrics.frame.size.height * scaleFactor -
249
- (nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) -
250
- (nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0)});
236
+ {outerFocusMetrics.frame.size.width * scaleFactor, outerFocusMetrics.frame.size.height * scaleFactor});
251
237
  m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Offset(
252
238
  {nudgeEdges.left ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor),
253
239
  nudgeEdges.top ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor),
@@ -258,15 +244,10 @@ void ComponentView::updateFocusLayoutMetrics() noexcept {
258
244
  if (m_focusPrimitive->m_focusInnerPrimitive) {
259
245
  auto innerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(true /*inner*/);
260
246
  m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Size(
261
- {innerFocusMetrics.frame.size.width * scaleFactor -
262
- (nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) -
263
- (nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0),
264
- innerFocusMetrics.frame.size.height * scaleFactor -
265
- (nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) -
266
- (nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0)});
247
+ {innerFocusMetrics.frame.size.width * scaleFactor, innerFocusMetrics.frame.size.height * scaleFactor});
267
248
  m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Offset(
268
- {nudgeEdges.left ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor,
269
- nudgeEdges.top ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor,
249
+ {nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : (-FOCUS_VISUAL_WIDTH * scaleFactor),
250
+ nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : (-FOCUS_VISUAL_WIDTH * scaleFactor),
270
251
  0.0f});
271
252
  m_focusPrimitive->m_focusInnerPrimitive->markNeedsUpdate();
272
253
  }
@@ -535,7 +516,33 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView:
535
516
  return m_outerVisual ? m_outerVisual : Visual();
536
517
  }
537
518
 
538
- facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept {
519
+ // If the focus visual would extend past the bounds of the hosting visual,
520
+ // then we will nudge the focus visual back inside the hosting visuals bounds.
521
+ facebook::react::RectangleEdges<bool> ComponentView::focusNudges() const noexcept {
522
+ facebook::react::RectangleEdges<bool> nudgeEdges;
523
+
524
+ // Always use outer focus metrics to determine if we need to nudge the focus rect over to fit
525
+ facebook::react::LayoutMetrics layoutMetrics = focusLayoutMetricsNoNudge(false /*inner*/);
526
+
527
+ Assert(m_componentHostingFocusVisual);
528
+
529
+ if (layoutMetrics.frame.origin.x < 0) {
530
+ nudgeEdges.left = true;
531
+ }
532
+ if (layoutMetrics.frame.origin.y < 0) {
533
+ nudgeEdges.top = true;
534
+ }
535
+ if (layoutMetrics.frame.getMaxX() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxX()) {
536
+ nudgeEdges.right = true;
537
+ }
538
+ if (layoutMetrics.frame.getMaxY() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxY()) {
539
+ nudgeEdges.bottom = true;
540
+ }
541
+
542
+ return nudgeEdges;
543
+ }
544
+
545
+ facebook::react::LayoutMetrics ComponentView::focusLayoutMetricsNoNudge(bool inner) const noexcept {
539
546
  facebook::react::LayoutMetrics layoutMetrics = m_layoutMetrics;
540
547
  layoutMetrics.frame.origin.x -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
541
548
  layoutMetrics.frame.origin.y -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
@@ -544,6 +551,28 @@ facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) con
544
551
  return layoutMetrics;
545
552
  }
546
553
 
554
+ facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept {
555
+ auto nudgeEdges = focusNudges();
556
+ auto layoutMetrics = focusLayoutMetricsNoNudge(inner);
557
+
558
+ if (nudgeEdges.left) {
559
+ layoutMetrics.frame.origin.x += FOCUS_VISUAL_WIDTH * 2;
560
+ layoutMetrics.frame.size.width -= FOCUS_VISUAL_WIDTH * 2;
561
+ }
562
+ if (nudgeEdges.top) {
563
+ layoutMetrics.frame.origin.y += FOCUS_VISUAL_WIDTH * 2;
564
+ layoutMetrics.frame.size.height -= FOCUS_VISUAL_WIDTH * 2;
565
+ }
566
+ if (nudgeEdges.right) {
567
+ layoutMetrics.frame.size.width -= FOCUS_VISUAL_WIDTH * 2;
568
+ }
569
+ if (nudgeEdges.bottom) {
570
+ layoutMetrics.frame.size.height -= FOCUS_VISUAL_WIDTH * 2;
571
+ }
572
+
573
+ return layoutMetrics;
574
+ }
575
+
547
576
  facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
548
577
  bool inner,
549
578
  const facebook::react::LayoutMetrics &layoutMetrics) const noexcept {
@@ -553,14 +582,17 @@ facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
553
582
  innerColor.m_platformColor.push_back(inner ? "FocusVisualSecondary" : "FocusVisualPrimary");
554
583
  metrics.borderColors.bottom = metrics.borderColors.left = metrics.borderColors.right = metrics.borderColors.top =
555
584
  innerColor;
556
- if (metrics.borderRadii.bottomLeft != 0)
557
- metrics.borderRadii.bottomLeft += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
558
- if (metrics.borderRadii.bottomRight != 0)
559
- metrics.borderRadii.bottomRight += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
560
- if (metrics.borderRadii.topLeft != 0)
561
- metrics.borderRadii.topLeft += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
562
- if (metrics.borderRadii.topRight != 0)
563
- metrics.borderRadii.topRight += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
585
+
586
+ metrics.borderRadii.bottomLeft =
587
+ (metrics.borderRadii.bottomLeft ? metrics.borderRadii.bottomLeft : FOCUS_VISUAL_RADIUS) +
588
+ FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
589
+ metrics.borderRadii.bottomRight =
590
+ (metrics.borderRadii.bottomRight ? metrics.borderRadii.bottomRight : FOCUS_VISUAL_RADIUS) +
591
+ FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
592
+ metrics.borderRadii.topLeft = (metrics.borderRadii.topLeft ? metrics.borderRadii.topLeft : FOCUS_VISUAL_RADIUS) +
593
+ FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
594
+ metrics.borderRadii.topRight = (metrics.borderRadii.topRight ? metrics.borderRadii.topRight : FOCUS_VISUAL_RADIUS) +
595
+ FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
564
596
 
565
597
  metrics.borderStyles.bottom = metrics.borderStyles.left = metrics.borderStyles.right = metrics.borderStyles.top =
566
598
  facebook::react::BorderStyle::Solid;
@@ -143,6 +143,8 @@ struct ComponentView : public ComponentViewT<
143
143
  void FinalizeTransform(
144
144
  facebook::react::LayoutMetrics const &layoutMetrics,
145
145
  const facebook::react::ViewProps &viewProps) noexcept;
146
+ facebook::react::RectangleEdges<bool> focusNudges() const noexcept;
147
+ facebook::react::LayoutMetrics focusLayoutMetricsNoNudge(bool inner) const noexcept;
146
148
  facebook::react::LayoutMetrics focusLayoutMetrics(bool inner) const noexcept;
147
149
  facebook::react::BorderMetrics focusBorderMetrics(bool inner, const facebook::react::LayoutMetrics &layoutMetrics)
148
150
  const noexcept;