diff --git a/.changeset/whole-keys-cry.md b/.changeset/whole-keys-cry.md new file mode 100644 index 0000000000..c0705c8070 --- /dev/null +++ b/.changeset/whole-keys-cry.md @@ -0,0 +1,6 @@ +--- +"@fluentui-react-native/components": patch +"@fluentui-react-native/callout": patch +--- + +Very initial framework for RNW fabric callout implementation diff --git a/packages/agentic-components/package.json b/packages/agentic-components/package.json index d2286f6be3..618f15c3e1 100644 --- a/packages/agentic-components/package.json +++ b/packages/agentic-components/package.json @@ -30,6 +30,7 @@ "update-snapshots": "fluentui-scripts jest -u" }, "dependencies": { + "@fluentui-react-native/callout": "workspace:*", "@fluentui-react-native/design": "workspace:*", "@fluentui-react-native/framework-base": "workspace:*" }, @@ -50,6 +51,7 @@ "test-renderer": "catalog:0.81" }, "peerDependencies": { + "@office-iss/react-native-win32": "^0.74.0 || ^0.81.0", "@types/react": "~18.2.0 || ~19.0.0 || ~19.1.4", "react": "18.2.0 || 19.0.0 || 19.1.4", "react-native": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.6", @@ -58,6 +60,9 @@ "react-native-windows": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0" }, "peerDependenciesMeta": { + "@office-iss/react-native-win32": { + "optional": true + }, "@types/react": { "optional": true }, @@ -69,7 +74,12 @@ } }, "furn": { - "jestPlatform": "macos" + "jestPlatform": "macos", + "knip": { + "ignoreDependencies": [ + "@fluentui-react-native/callout" + ] + } }, "rnx-kit": { "kitType": "library", diff --git a/packages/agentic-components/storybook/Directory.Build.props b/packages/agentic-components/storybook/Directory.Build.props new file mode 100644 index 0000000000..037a06abb3 --- /dev/null +++ b/packages/agentic-components/storybook/Directory.Build.props @@ -0,0 +1,11 @@ + + + + + + yarn exec rnc-cli autolink-windows + false + yarn exec rnc-cli codegen-windows + + + \ No newline at end of file diff --git a/packages/agentic-components/tsconfig.json b/packages/agentic-components/tsconfig.json index 14a3238c30..6c3e991296 100644 --- a/packages/agentic-components/tsconfig.json +++ b/packages/agentic-components/tsconfig.json @@ -9,6 +9,9 @@ "include": ["src"], "exclude": ["**/*.stories.ts", "**/*.stories.tsx"], "references": [ + { + "path": "../components/Callout/tsconfig.json" + }, { "path": "../agentic-design/tsconfig.json" }, diff --git a/packages/components/Callout/react-native.config.cjs b/packages/components/Callout/react-native.config.cjs new file mode 100644 index 0000000000..5046885ab4 --- /dev/null +++ b/packages/components/Callout/react-native.config.cjs @@ -0,0 +1,15 @@ +module.exports = { + dependency: { + platforms: { + windows: { + sourceDir: 'windows', + projects: [ + { + projectFile: 'Callout/Callout.vcxproj', + directDependency: true, + }, + ], + }, + }, + }, +}; diff --git a/packages/components/Callout/src/Callout.stories.tsx b/packages/components/Callout/src/Callout.stories.tsx index cec96831ad..87ddaf51e9 100644 --- a/packages/components/Callout/src/Callout.stories.tsx +++ b/packages/components/Callout/src/Callout.stories.tsx @@ -66,7 +66,7 @@ const CalloutExample = ({ defaultVisible = false, onDismiss, onShow, showWindowC {visible && ( - + Fabric Callout This content is hosted in a separate native macOS window. {showWindowCommands && ( @@ -127,7 +127,7 @@ const PlacementExample = (props: CalloutProps) => { onDismiss={() => setSelectedIndex(undefined)} target={targetRefs[selectedIndex]} > - + {placements[selectedIndex].label} Click outside the native window to dismiss it. diff --git a/packages/components/Callout/windows/Callout/Callout.cpp b/packages/components/Callout/windows/Callout/Callout.cpp new file mode 100644 index 0000000000..367299f186 --- /dev/null +++ b/packages/components/Callout/windows/Callout/Callout.cpp @@ -0,0 +1,558 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" + +#include "Callout.h" + +#include +#include +#include +#include +#include + +namespace winrt::FluentUI::Callout { + +enum class DirectionalHint { + LeftTopEdge, + LeftCenter, + LeftBottomEdge, + TopLeftEdge, + TopAutoEdge, + TopCenter, + TopRightEdge, + RightTopEdge, + RightCenter, + RightBottomEdge, + BottomLeftEdge, + BottomAutoEdge, + BottomCenter, + BottomRightEdge +}; + +DirectionalHint parseDirectionHint(std::string &directionHint) { + if (directionHint == "leftTopEdge") { + return DirectionalHint::LeftTopEdge; + } else if (directionHint == "leftCenter") { + return DirectionalHint::LeftCenter; + } else if (directionHint == "leftBottomEdge") { + return DirectionalHint::LeftBottomEdge; + } else if (directionHint == "topLeftEdge") { + return DirectionalHint::TopLeftEdge; + } else if (directionHint == "topAutoEdge") { + return DirectionalHint::TopAutoEdge; + } else if (directionHint == "topCenter") { + return DirectionalHint::TopCenter; + } else if (directionHint == "topRightEdge") { + return DirectionalHint::TopRightEdge; + } else if (directionHint == "rightTopEdge") { + return DirectionalHint::RightTopEdge; + } else if (directionHint == "rightCenter") { + return DirectionalHint::RightCenter; + } else if (directionHint == "rightBottomEdge") { + return DirectionalHint::RightBottomEdge; + } else if (directionHint == "bottomLeftEdge") { + return DirectionalHint::BottomLeftEdge; + } else if (directionHint == "bottomAutoEdge") { + return DirectionalHint::BottomAutoEdge; + } else if (directionHint == "bottomCenter") { + return DirectionalHint::BottomCenter; + } else if (directionHint == "bottomRightEdge") { + return DirectionalHint::BottomRightEdge; + } + + return DirectionalHint::LeftTopEdge; +} + +struct CalloutComponentView + : public winrt::implements, + Codegen::BaseRCTCallout { + ~CalloutComponentView() { + if (m_popup && !m_popup.IsClosed()) { + /* + // Unregister closing event handler + if (m_appWindowClosingToken) + { + m_rnWindow.AppWindow().Closing(m_appWindowClosingToken); + m_appWindowClosingToken = {}; + } + */ + + // Hide popup + if (m_popup.IsVisible()) { + m_popup.Hide(); + } + + if (m_rnWindow.AppWindow()) { + m_rnWindow.Close(); + m_rnWindow = nullptr; + } + + // Close bridge + m_popup.Close(); + m_popup = nullptr; + } + } + + void UpdateProps(const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::com_ptr &newProps, + const winrt::com_ptr + &oldProps) noexcept override { + if (!oldProps || newProps->directionalHint != oldProps->directionalHint) { + m_directionalHint = + newProps->directionalHint + ? parseDirectionHint(newProps->directionalHint.value()) + : DirectionalHint::LeftTopEdge; + + auto portal = view.as< + winrt::Microsoft::ReactNative::Composition::PortalComponentView>(); + if (portal.ContentRoot().Children().Size() != 0) { + AdjustWindowSize( + portal.ContentRoot().Children().GetAt(0).LayoutMetrics()); + } + } + + __super::UpdateProps(view, newProps, oldProps); + } + + void InitializePortalViewComponent( + const winrt::Microsoft::ReactNative::Composition::PortalComponentView + &portalComponentView) noexcept { + m_reactContext = portalComponentView.ReactContext(); + + portalComponentView.Mounted([](const auto & /*sender*/, const auto &view) { + view.UserData().as()->OnMounted(view); + }); + portalComponentView.Unmounted( + [](const auto & /*sender*/, const auto &view) { + view.UserData().as()->OnUnmounted(view); + }); + } + + void HandleFocusWindowCommand() noexcept override { + // nyi + } + + // You must provide an implementation of this method to handle the + // "blurWindow" command + void HandleBlurWindowCommand() noexcept override { + // nyi + } + + void MountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::MountChildComponentViewArgs + &args) noexcept override { + AdjustWindowSize(args.Child().LayoutMetrics()); + // TODO These asserts are currently hit if the root view of the callout is collapsed. + // Need to handle this case + // assert(args.Index() == 0); + // assert(!m_childLayoutMetricsToken); + m_childLayoutMetricsToken = args.Child().LayoutMetricsChanged( + [wkThis = get_weak()]( + auto &/*sender*/, + const winrt::Microsoft::ReactNative::LayoutMetricsChangedArgs + &layoutMetricsChangedArgs) { + if (auto strongThis = wkThis.get()) { + strongThis->AdjustWindowSize( + layoutMetricsChangedArgs.NewLayoutMetrics()); + } + }); + } + + void UnmountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs + &args) noexcept override { + // TODO These asserts are currently hit if the root view of the callout is collapsed. + // Need to handle this case + // assert(args.Index() == 0); + // assert(m_childLayoutMetricsToken); + args.Child().LayoutMetricsChanged(m_childLayoutMetricsToken); + // m_childLayoutMetricsToken.value = 0; + } + +private: + void + OnMounted(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + assert(!m_mounted); + m_mounted = true; + + CreatePopup(view); + + m_showQueued = true; + + m_reactContext.UIDispatcher().Post( + [wkThis = get_weak(), wkView = winrt::weak_ref(view)]() { + if (auto strongThis = wkThis.get()) { + strongThis->m_showQueued = false; + + if (!strongThis->m_mounted) { + return; + } + if (auto v = wkView.get()) { + strongThis->Show(); + } + } + }); + } + + void OnUnmounted( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/) noexcept { + assert(m_mounted); + m_mounted = false; + } + + void OnLightDismissDismissed( + const winrt::Microsoft::UI::Input::InputLightDismissAction &, + const winrt::Microsoft::UI::Input::InputLightDismissEventArgs &) { + HidePopup(); + } + + void HidePopup() { + if (!m_popup) + return; + m_popup.Hide(); + if (auto eventEmitter = EventEmitter()) + eventEmitter->onDismiss({}); + } + + void CreatePopup( + const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + if (m_popup) + return; + + auto portal = view.as< + winrt::Microsoft::ReactNative::Composition::PortalComponentView>(); + m_popup = winrt::Microsoft::UI::Content::DesktopPopupSiteBridge::Create( + portal.Parent() + .as() + .Root() + .ReactNativeIsland() + .Island()); + + assert(!m_rnWindow); + m_rnWindow = winrt::Microsoft::ReactNative::ReactNativeWindow:: + CreateFromContentSiteBridgeAndIsland( + m_popup, + winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal( + portal)); + m_rnWindow.ResizePolicy( + winrt::Microsoft::ReactNative::ContentSizePolicy::None); + + auto inputLightDismissAction = + winrt::Microsoft::UI::Input::InputLightDismissAction::GetForWindowId( + m_rnWindow.AppWindow().Id()); + inputLightDismissAction.Dismissed( + {get_weak(), &CalloutComponentView::OnLightDismissDismissed}); + + if (portal.ContentRoot().Children().Size()) { + AdjustWindowSize( + portal.ContentRoot().Children().GetAt(0).LayoutMetrics()); + } + } + + void RegisterLightDismissAction() noexcept {} + + void Show() noexcept { + m_popup.Show(); + + winrt::Microsoft::UI::Input::InputFocusController::GetForIsland( + m_rnWindow.ReactNativeIsland().Island()) + .TrySetFocus(); + m_rnWindow.ReactNativeIsland().NavigateFocus( + winrt::Microsoft::ReactNative::FocusNavigationRequest:: + FocusNavigationRequest( + winrt::Microsoft::ReactNative::FocusNavigationReason::First)); + + if (auto eventEmitter = EventEmitter()) { + eventEmitter->onShow({}); + } + } + + void AdjustWindowSize(const winrt::Microsoft::ReactNative::LayoutMetrics + &layoutMetrics) noexcept { + if (!m_rnWindow) { + return; + } + + if (layoutMetrics.Frame.Width == 0 && layoutMetrics.Frame.Height == 0) { + return; + } + + // Calculate physical pixels from DIPs + int32_t clientWidthPx = static_cast( + layoutMetrics.Frame.Width * layoutMetrics.PointScaleFactor); + int32_t clientHeightPx = static_cast( + layoutMetrics.Frame.Height * layoutMetrics.PointScaleFactor); + + // Size the client area directly + m_rnWindow.AppWindow().ResizeClient({clientWidthPx, clientHeightPx}); + + // Target can be either a view tag of a view to anchor to, or a string for + // an anchor id + if (Props()->target.Type() == + winrt::Microsoft::ReactNative::JSValueType::Int64) { + auto targetView = winrt::Microsoft::ReactNative::Composition:: + CompositionUIService::ComponentFromReactTag( + m_reactContext.Handle(), Props()->target.AsInt64()); + auto targetPos = ViewToScreenOffset(targetView); + + POINT anchorPoint{targetPos.X, targetPos.Y}; + SIZE windowSize{clientWidthPx, clientHeightPx}; + + RECT excludeRect{targetPos.X, targetPos.Y, + targetPos.X + targetView.LayoutMetrics().Frame.Width, + targetPos.Y + targetView.LayoutMetrics().Frame.Height}; + + UINT flags = 0; + + if (m_directionalHint == DirectionalHint::LeftTopEdge) { + flags = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORIZONTAL; + } else if (m_directionalHint == DirectionalHint::LeftCenter) { + flags = TPM_LEFTALIGN | TPM_VCENTERALIGN | TPM_HORIZONTAL; + } else if (m_directionalHint == DirectionalHint::LeftBottomEdge) { + flags = TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_HORIZONTAL; + } else if (m_directionalHint == DirectionalHint::TopLeftEdge) { + flags = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::TopAutoEdge) { + flags = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::TopCenter) { + flags = TPM_CENTERALIGN | TPM_TOPALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::TopRightEdge) { + flags = TPM_RIGHTALIGN | TPM_TOPALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::RightTopEdge) { + flags = TPM_RIGHTALIGN | TPM_TOPALIGN | TPM_HORIZONTAL; + } else if (m_directionalHint == DirectionalHint::RightCenter) { + flags = TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_HORIZONTAL; + } else if (m_directionalHint == DirectionalHint::RightBottomEdge) { + flags = TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_HORIZONTAL; + } else if (m_directionalHint == DirectionalHint::BottomLeftEdge) { + flags = TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::BottomAutoEdge) { + flags = TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::BottomCenter) { + flags = TPM_CENTERALIGN | TPM_BOTTOMALIGN | TPM_VERTICAL; + } else if (m_directionalHint == DirectionalHint::BottomRightEdge) { + flags = TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_VERTICAL; + } + + flags |= TPM_WORKAREA; + + RECT finalPos; + + CalculatePopupWindowPosition(&anchorPoint, &windowSize, flags, + &excludeRect, &finalPos); + + m_rnWindow.AppWindow().Move({finalPos.left, finalPos.top}); + } else { + // TODO target named anchors + m_rnWindow.AppWindow().Move({0, 0}); + } + }; + + winrt::Windows::Graphics::PointInt32 + ViewToScreenOffset(const winrt::Microsoft::ReactNative::ComponentView &view) { + winrt::Windows::Foundation::Point pt{ + (view.LayoutMetrics().Frame.X) * view.LayoutMetrics().PointScaleFactor, + (view.LayoutMetrics().Frame.Y) * view.LayoutMetrics().PointScaleFactor}; + + for (auto p = view.Parent(); p; p = p.Parent()) { + pt.X += p.LayoutMetrics().Frame.X * p.LayoutMetrics().PointScaleFactor; + pt.Y += p.LayoutMetrics().Frame.Y * p.LayoutMetrics().PointScaleFactor; + } + + auto root = + view.as() + .Root(); + auto cc = root.ReactNativeIsland().Island().CoordinateConverter(); + return cc.ConvertLocalToScreen(pt); + } + + DirectionalHint m_directionalHint{DirectionalHint::LeftTopEdge}; + bool m_showQueued{false}; + bool m_mounted{false}; + winrt::event_token m_childLayoutMetricsToken; + winrt::Microsoft::UI::Content::DesktopPopupSiteBridge m_popup{nullptr}; + winrt::Microsoft::ReactNative::ReactNativeWindow m_rnWindow{nullptr}; + winrt::Microsoft::ReactNative::ReactContext m_reactContext{nullptr}; +}; + +} // namespace winrt::FluentUI::Callout + +namespace winrt::FluentUI::Callout::Codegen { + +// Copy/Paste of codegen'd RegisterRCTCalloutNativeComponent, but uses "Callout" +// to register the component instead of RCTCallout RN has special cased RCT* to +// be just *. But the codegen doesn't know that. We should probably eventually +// move away from using RCT prefixes everywhere too. +template +void RegisterRCTCalloutNativeComponent2( + winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder, + std::function + builderCallback) noexcept { + packageBuilder.as() + .AddViewComponent(L"Callout", [builderCallback]( + winrt::Microsoft::ReactNative:: + IReactViewComponentBuilder const + &builder) noexcept { + auto compBuilder = + builder.as(); + + builder.SetCreateProps( + [](winrt::Microsoft::ReactNative::ViewProps props, + const winrt::Microsoft::ReactNative::IComponentProps + &cloneFrom) noexcept { + return winrt::make(props, cloneFrom); + }); + + builder.SetUpdatePropsHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::IComponentProps &newProps, + const winrt::Microsoft::ReactNative::IComponentProps + &oldProps) noexcept { + auto userData = view.UserData().as(); + userData->UpdateProps( + view, newProps ? newProps.as() : nullptr, + oldProps ? oldProps.as() : nullptr); + }); + + compBuilder.SetUpdateLayoutMetricsHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::LayoutMetrics + &newLayoutMetrics, + const winrt::Microsoft::ReactNative::LayoutMetrics + &oldLayoutMetrics) noexcept { + auto userData = view.UserData().as(); + userData->UpdateLayoutMetrics(view, newLayoutMetrics, + oldLayoutMetrics); + }); + + builder.SetUpdateEventEmitterHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::EventEmitter + &eventEmitter) noexcept { + auto userData = view.UserData().as(); + userData->UpdateEventEmitter( + std::make_shared(eventEmitter)); + }); + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS ( + &TUserData::FinalizeUpdate != + &BaseRCTCallout::FinalizeUpdate) { + builder.SetFinalizeUpdateHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + winrt::Microsoft::ReactNative::ComponentViewUpdateMask + mask) noexcept { + auto userData = view.UserData().as(); + userData->FinalizeUpdate(view, mask); + }); + } + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::UpdateState != + &BaseRCTCallout< + TUserData>:: + UpdateState) { + builder.SetUpdateStateHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::IComponentState + &newState) noexcept { + auto userData = view.UserData().as(); + userData->UpdateState(view, newState); + }); + } + + builder.SetCustomCommandHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::HandleCommandArgs + &args) noexcept { + auto userData = view.UserData().as(); + userData->HandleCommand(view, args); + }); + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS ( + &TUserData::MountChildComponentView != + &BaseRCTCallout::MountChildComponentView) { + builder.SetMountChildComponentViewHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative:: + MountChildComponentViewArgs &args) noexcept { + auto userData = view.UserData().as(); + return userData->MountChildComponentView(view, args); + }); + } + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS ( + &TUserData::UnmountChildComponentView != + &BaseRCTCallout::UnmountChildComponentView) { + builder.SetUnmountChildComponentViewHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative:: + UnmountChildComponentViewArgs &args) noexcept { + auto userData = view.UserData().as(); + return userData->UnmountChildComponentView(view, args); + }); + } + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS ( + &TUserData::CreateAutomationPeer != + &BaseRCTCallout::CreateAutomationPeer) { + builder.SetCreateAutomationPeerHandler( + [](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::CreateAutomationPeerArgs + &args) noexcept { + auto userData = view.UserData().as(); + return userData->CreateAutomationPeer(view, args); + }); + } + + compBuilder.SetViewComponentViewInitializer( + [](const winrt::Microsoft::ReactNative::ComponentView + &view) noexcept { + auto userData = winrt::make_self(); + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS ( + &TUserData::Initialize != + &BaseRCTCallout::Initialize) { + userData->Initialize(view); + } + view.UserData(*userData); + }); + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS ( + &TUserData::CreateVisual != + &BaseRCTCallout::CreateVisual) { + compBuilder.SetCreateVisualHandler( + [](const winrt::Microsoft::ReactNative::ComponentView + &view) noexcept { + auto userData = view.UserData().as(); + return userData->CreateVisual(view); + }); + } + + // Allow app to further customize the builder + if (builderCallback) { + builderCallback(compBuilder); + } + }); +} + +} // namespace winrt::FluentUI::Callout::Codegen + +void RegisterCalloutComponentView( + winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) { + winrt::FluentUI::Callout::Codegen::RegisterRCTCalloutNativeComponent2< + winrt::FluentUI::Callout::CalloutComponentView>( + packageBuilder, [](const winrt::Microsoft::ReactNative::Composition:: + IReactCompositionViewComponentBuilder &builder) { + builder.SetPortalComponentViewInitializer( + [](const winrt::Microsoft::ReactNative::Composition:: + PortalComponentView &portalComponentView) noexcept { + auto userData = winrt::make_self< + winrt::FluentUI::Callout::CalloutComponentView>(); + userData->InitializePortalViewComponent(portalComponentView); + portalComponentView.UserData(*userData); + }); + }); +}; \ No newline at end of file diff --git a/packages/components/Callout/windows/Callout/Callout.def b/packages/components/Callout/windows/Callout/Callout.def new file mode 100644 index 0000000000..24e7c1235c --- /dev/null +++ b/packages/components/Callout/windows/Callout/Callout.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/packages/components/Callout/windows/Callout/Callout.h b/packages/components/Callout/windows/Callout/Callout.h new file mode 100644 index 0000000000..b1f8e8d61f --- /dev/null +++ b/packages/components/Callout/windows/Callout/Callout.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +#include "pch.h" + +#include "NativeModules.h" + +#include "../codegen/react/components/FRNCalloutSpec/RCTCallout.g.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace winrt::FluentUI::Callout { + +} // namespace winrt::FluentUI::Callout + +void RegisterCalloutComponentView( + winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder); diff --git a/packages/components/Callout/windows/Callout/Callout.vcxproj b/packages/components/Callout/windows/Callout/Callout.vcxproj new file mode 100644 index 0000000000..3799c9cb2a --- /dev/null +++ b/packages/components/Callout/windows/Callout/Callout.vcxproj @@ -0,0 +1,151 @@ + + + + + + true + true + true + {FE36524A-FE8C-456A-9368-08FB1EB16CF8} + FluentUICallout + FluentUI.Callout + en-US + 16.0 + false + Windows Store + 10.0 + 10.0.19041.0 + 10.0.17763.0 + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ + + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + DynamicLibrary + Unicode + false + + + true + true + + + false + true + false + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + 4453;28204 + _WINRT_DLL;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + User32.lib;%(AdditionalDependencies) + Console + true + Callout.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + false + + + + + + ReactPackageProvider.idl + + + + + + + Create + + + ReactPackageProvider.idl + + + + + + + + + + + + + + + + + This project references targets in your node_modules\react-native-windows folder that are missing. The missing file is {0}. + + + + + diff --git a/packages/components/Callout/windows/Callout/ReactPackageProvider.cpp b/packages/components/Callout/windows/Callout/ReactPackageProvider.cpp new file mode 100644 index 0000000000..bac1881140 --- /dev/null +++ b/packages/components/Callout/windows/Callout/ReactPackageProvider.cpp @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" + +#include "ReactPackageProvider.h" +#if __has_include("ReactPackageProvider.g.cpp") +#include "ReactPackageProvider.g.cpp" +#endif + +#include "Callout.h" + +using namespace winrt::Microsoft::ReactNative; + +namespace winrt::FluentUI::Callout::implementation { + +void ReactPackageProvider::CreatePackage( + IReactPackageBuilder const &packageBuilder) noexcept { + AddAttributedModules(packageBuilder, true); + +#ifdef RNW_NEW_ARCH + RegisterCalloutComponentView(packageBuilder); +#endif // #ifdef RNW_NEW_ARCH +} + +} // namespace winrt::FluentUI::Callout::implementation diff --git a/packages/components/Callout/windows/Callout/ReactPackageProvider.h b/packages/components/Callout/windows/Callout/ReactPackageProvider.h new file mode 100644 index 0000000000..ee4a609103 --- /dev/null +++ b/packages/components/Callout/windows/Callout/ReactPackageProvider.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +#include "ReactPackageProvider.g.h" + +using namespace winrt::Microsoft::ReactNative; + +namespace winrt::FluentUI::Callout::implementation { + +struct ReactPackageProvider : ReactPackageProviderT { + ReactPackageProvider() = default; + + void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; +}; + +} // namespace winrt::FluentUI::Callout::implementation + +namespace winrt::FluentUI::Callout::factory_implementation { + +struct ReactPackageProvider + : ReactPackageProviderT {}; + +} // namespace winrt::FluentUI::Callout::factory_implementation diff --git a/packages/components/Callout/windows/Callout/ReactPackageProvider.idl b/packages/components/Callout/windows/Callout/ReactPackageProvider.idl new file mode 100644 index 0000000000..cd726b6e61 --- /dev/null +++ b/packages/components/Callout/windows/Callout/ReactPackageProvider.idl @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace FluentUI.Callout +{ + [webhosthidden] + [default_interface] + runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider + { + ReactPackageProvider(); + }; +} diff --git a/packages/components/Callout/windows/Callout/codegen/.clang-format b/packages/components/Callout/windows/Callout/codegen/.clang-format new file mode 100644 index 0000000000..9d159247d5 --- /dev/null +++ b/packages/components/Callout/windows/Callout/codegen/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false diff --git a/packages/components/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec/RCTCallout.g.h b/packages/components/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec/RCTCallout.g.h new file mode 100644 index 0000000000..be4153ee8f --- /dev/null +++ b/packages/components/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec/RCTCallout.g.h @@ -0,0 +1,359 @@ + +/* + * This file is auto-generated from RCTCalloutNativeComponent spec file in flow / TypeScript. + */ +// clang-format off +#pragma once + +#include + +#ifdef RNW_NEW_ARCH +#include + +#include +#include +#endif // #ifdef RNW_NEW_ARCH + +#ifdef RNW_NEW_ARCH + +namespace winrt::FluentUI::Callout::Codegen { + +REACT_STRUCT(RCTCalloutSpec_RCTCalloutProps_anchorRect) +struct RCTCalloutSpec_RCTCalloutProps_anchorRect { + REACT_FIELD(screenX) + double screenX{}; + + REACT_FIELD(screenY) + double screenY{}; + + REACT_FIELD(width) + double width{}; + + REACT_FIELD(height) + double height{}; +}; + +REACT_STRUCT(RCTCalloutProps) +struct RCTCalloutProps : winrt::implements { + RCTCalloutProps(winrt::Microsoft::ReactNative::ViewProps props, const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom) + : ViewProps(props) + { + if (cloneFrom) { + auto cloneFromProps = cloneFrom.as(); + accessibilityLabel = cloneFromProps->accessibilityLabel; + accessibilityOnShowAnnouncement = cloneFromProps->accessibilityOnShowAnnouncement; + anchorRect = cloneFromProps->anchorRect; + dismissBehaviors = cloneFromProps->dismissBehaviors; + doNotTakePointerCapture = cloneFromProps->doNotTakePointerCapture; + focusable = cloneFromProps->focusable; + isBeakVisible = cloneFromProps->isBeakVisible; + maxHeight = cloneFromProps->maxHeight; + maxWidth = cloneFromProps->maxWidth; + setInitialFocus = cloneFromProps->setInitialFocus; + testID = cloneFromProps->testID; + directionalHint = cloneFromProps->directionalHint; + target = cloneFromProps->target.Copy(); + onRestoreFocus = cloneFromProps->onRestoreFocus; + onDismiss = cloneFromProps->onDismiss; + onShow = cloneFromProps->onShow; + } + } + + void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept { + winrt::Microsoft::ReactNative::ReadProp(hash, propName, value, *this); + } + + REACT_FIELD(accessibilityLabel) + std::optional accessibilityLabel; + + REACT_FIELD(accessibilityOnShowAnnouncement) + std::optional accessibilityOnShowAnnouncement; + + REACT_FIELD(anchorRect) + std::optional anchorRect; + + REACT_FIELD(dismissBehaviors) + std::optional> dismissBehaviors; + + REACT_FIELD(doNotTakePointerCapture) + std::optional doNotTakePointerCapture{}; + + REACT_FIELD(focusable) + std::optional focusable{}; + + REACT_FIELD(isBeakVisible) + std::optional isBeakVisible{}; + + REACT_FIELD(maxHeight) + std::optional maxHeight{}; + + REACT_FIELD(maxWidth) + std::optional maxWidth{}; + + REACT_FIELD(setInitialFocus) + std::optional setInitialFocus{}; + + REACT_FIELD(testID) + std::optional testID; + + REACT_FIELD(directionalHint) + std::optional directionalHint; + + REACT_FIELD(target) + winrt::Microsoft::ReactNative::JSValue target{nullptr}; + + // These fields can be used to determine if JS has registered for this event + REACT_FIELD(onRestoreFocus) + bool onRestoreFocus{false}; + + REACT_FIELD(onDismiss) + bool onDismiss{false}; + + REACT_FIELD(onShow) + bool onShow{false}; + + const winrt::Microsoft::ReactNative::ViewProps ViewProps; +}; + +REACT_STRUCT(RCTCalloutSpec_onShow) +struct RCTCalloutSpec_onShow { + REACT_FIELD(target) + int32_t target{}; +}; + +REACT_STRUCT(RCTCalloutSpec_onDismiss) +struct RCTCalloutSpec_onDismiss { + REACT_FIELD(target) + int32_t target{}; +}; + +REACT_STRUCT(RCTCalloutSpec_onRestoreFocus) +struct RCTCalloutSpec_onRestoreFocus { + REACT_FIELD(target) + int32_t target{}; + + REACT_FIELD(containsFocus) + bool containsFocus{}; +}; + +struct RCTCalloutEventEmitter { + RCTCalloutEventEmitter(const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) + : m_eventEmitter(eventEmitter) {} + + using OnRestoreFocus = RCTCalloutSpec_onRestoreFocus; + using OnDismiss = RCTCalloutSpec_onDismiss; + using OnShow = RCTCalloutSpec_onShow; + + void onRestoreFocus(OnRestoreFocus &&value) const { + m_eventEmitter.DispatchEvent(L"restoreFocus", [value = std::move(value)](const winrt::Microsoft::ReactNative::IJSValueWriter writer) { + winrt::Microsoft::ReactNative::WriteValue(writer, value); + }); + } + + void onDismiss(OnDismiss &&value) const { + m_eventEmitter.DispatchEvent(L"dismiss", [value = std::move(value)](const winrt::Microsoft::ReactNative::IJSValueWriter writer) { + winrt::Microsoft::ReactNative::WriteValue(writer, value); + }); + } + + void onShow(OnShow &&value) const { + m_eventEmitter.DispatchEvent(L"show", [value = std::move(value)](const winrt::Microsoft::ReactNative::IJSValueWriter writer) { + winrt::Microsoft::ReactNative::WriteValue(writer, value); + }); + } + + private: + winrt::Microsoft::ReactNative::EventEmitter m_eventEmitter{nullptr}; +}; + +template +struct BaseRCTCallout { + + virtual void UpdateProps( + const winrt::Microsoft::ReactNative::ComponentView &/*view*/, + const winrt::com_ptr &newProps, + const winrt::com_ptr &/*oldProps*/) noexcept { + m_props = newProps; + } + + // UpdateLayoutMetrics will only be called if this method is overridden + virtual void UpdateLayoutMetrics( + const winrt::Microsoft::ReactNative::ComponentView &/*view*/, + const winrt::Microsoft::ReactNative::LayoutMetrics &/*newLayoutMetrics*/, + const winrt::Microsoft::ReactNative::LayoutMetrics &/*oldLayoutMetrics*/) noexcept { + } + + // UpdateState will only be called if this method is overridden + virtual void UpdateState( + const winrt::Microsoft::ReactNative::ComponentView &/*view*/, + const winrt::Microsoft::ReactNative::IComponentState &/*newState*/) noexcept { + } + + virtual void UpdateEventEmitter(const std::shared_ptr &eventEmitter) noexcept { + m_eventEmitter = eventEmitter; + } + + // MountChildComponentView will only be called if this method is overridden + virtual void MountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/, + const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &/*args*/) noexcept { + } + + // UnmountChildComponentView will only be called if this method is overridden + virtual void UnmountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/, + const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &/*args*/) noexcept { + } + + // Initialize will only be called if this method is overridden + virtual void Initialize(const winrt::Microsoft::ReactNative::ComponentView &/*view*/) noexcept { + } + + // CreateVisual will only be called if this method is overridden + virtual winrt::Microsoft::UI::Composition::Visual CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + return view.as().Compositor().CreateSpriteVisual(); + } + + // FinalizeUpdate will only be called if this method is overridden + virtual void FinalizeUpdate(const winrt::Microsoft::ReactNative::ComponentView &/*view*/, + winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) noexcept { + } + + // CreateAutomationPeer will only be called if this method is overridden + virtual winrt::Windows::Foundation::IInspectable CreateAutomationPeer(const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::CreateAutomationPeerArgs& /*args*/) noexcept { + return nullptr; + } + + // You must provide an implementation of this method to handle the "focusWindow" command + virtual void HandleFocusWindowCommand() noexcept = 0; + + // You must provide an implementation of this method to handle the "blurWindow" command + virtual void HandleBlurWindowCommand() noexcept = 0; + + void HandleCommand(const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept { + auto userData = view.UserData().as(); + auto commandName = args.CommandName(); + if (commandName == L"focusWindow") { + + userData->HandleFocusWindowCommand(); + return; + } + + if (commandName == L"blurWindow") { + + userData->HandleBlurWindowCommand(); + return; + } + } + + const std::shared_ptr& EventEmitter() const { return m_eventEmitter; } + const winrt::com_ptr& Props() const { return m_props; } + +private: + winrt::com_ptr m_props; + std::shared_ptr m_eventEmitter; +}; + +template +void RegisterRCTCalloutNativeComponent( + winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder, + std::function builderCallback) noexcept { + packageBuilder.as().AddViewComponent( + L"Callout", [builderCallback](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { + auto compBuilder = builder.as(); + + builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props, + const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom) noexcept { + return winrt::make(props, cloneFrom); + }); + + builder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::IComponentProps &newProps, + const winrt::Microsoft::ReactNative::IComponentProps &oldProps) noexcept { + auto userData = view.UserData().as(); + userData->UpdateProps(view, newProps ? newProps.as() : nullptr, oldProps ? oldProps.as() : nullptr); + }); + + compBuilder.SetUpdateLayoutMetricsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics, + const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) noexcept { + auto userData = view.UserData().as(); + userData->UpdateLayoutMetrics(view, newLayoutMetrics, oldLayoutMetrics); + }); + + builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) noexcept { + auto userData = view.UserData().as(); + userData->UpdateEventEmitter(std::make_shared(eventEmitter)); + }); + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::FinalizeUpdate != &BaseRCTCallout::FinalizeUpdate) { + builder.SetFinalizeUpdateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + winrt::Microsoft::ReactNative::ComponentViewUpdateMask mask) noexcept { + auto userData = view.UserData().as(); + userData->FinalizeUpdate(view, mask); + }); + } + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::UpdateState != &BaseRCTCallout::UpdateState) { + builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { + auto userData = view.UserData().as(); + userData->UpdateState(view, newState); + }); + } + + builder.SetCustomCommandHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept { + auto userData = view.UserData().as(); + userData->HandleCommand(view, args); + }); + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::MountChildComponentView != &BaseRCTCallout::MountChildComponentView) { + builder.SetMountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept { + auto userData = view.UserData().as(); + return userData->MountChildComponentView(view, args); + }); + } + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::UnmountChildComponentView != &BaseRCTCallout::UnmountChildComponentView) { + builder.SetUnmountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &args) noexcept { + auto userData = view.UserData().as(); + return userData->UnmountChildComponentView(view, args); + }); + } + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::CreateAutomationPeer != &BaseRCTCallout::CreateAutomationPeer) { + builder.SetCreateAutomationPeerHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, + const winrt::Microsoft::ReactNative::CreateAutomationPeerArgs& args) noexcept { + auto userData = view.UserData().as(); + return userData->CreateAutomationPeer(view, args); + }); + } + + compBuilder.SetViewComponentViewInitializer([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + auto userData = winrt::make_self(); + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::Initialize != &BaseRCTCallout::Initialize) { + userData->Initialize(view); + } + view.UserData(*userData); + }); + + if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::CreateVisual != &BaseRCTCallout::CreateVisual) { + compBuilder.SetCreateVisualHandler([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + auto userData = view.UserData().as(); + return userData->CreateVisual(view); + }); + } + + // Allow app to further customize the builder + if (builderCallback) { + builderCallback(compBuilder); + } + }); +} + +} // namespace winrt::FluentUI::Callout::Codegen + +#endif // #ifdef RNW_NEW_ARCH diff --git a/packages/components/Callout/windows/Callout/packages.lock.json b/packages/components/Callout/windows/Callout/packages.lock.json new file mode 100644 index 0000000000..17e459d260 --- /dev/null +++ b/packages/components/Callout/windows/Callout/packages.lock.json @@ -0,0 +1,151 @@ +{ + "version": 1, + "dependencies": { + "native,Version=v0.0": { + "boost": { + "type": "Direct", + "requested": "[1.84.0, )", + "resolved": "1.84.0", + "contentHash": "4el2YP3cNJDVFPdzOso+LxGvdWP2rHxML4siq8VdonNypW2m4q503tHfCj6vK0L1UfxioE2hpFGb4ITEua73tg==" + }, + "Microsoft.ReactNative": { + "type": "Direct", + "requested": "[0.81.32, )", + "resolved": "0.81.32", + "contentHash": "Ks1M8X7hk/yFF0LV4PymQelr1A2PqselgqBIuJIvJgI0bnO0OMXDXqj6C1SDJCAaKUa5U3/G78h+yUPa4EKsJQ==" + }, + "Microsoft.ReactNative.Cxx": { + "type": "Direct", + "requested": "[0.81.32, )", + "resolved": "0.81.32", + "contentHash": "7sk1LmAne4vDt5BWUg2drRw85ktACDdBLBMGK2y0iXWwZjayhmzkMBmVhX4zoN6t+bYu/Z2/LfyOVqQJCok7Sg==", + "dependencies": { + "Microsoft.ReactNative": "0.81.32" + } + }, + "Microsoft.VCRTForwarders.140": { + "type": "Direct", + "requested": "[1.0.2-rc, )", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, + "Microsoft.Windows.CppWinRT": { + "type": "Direct", + "requested": "[2.0.230706.1, )", + "resolved": "2.0.230706.1", + "contentHash": "l0D7oCw/5X+xIKHqZTi62TtV+1qeSz7KVluNFdrJ9hXsst4ghvqQ/Yhura7JqRdZWBXAuDS0G0KwALptdoxweQ==" + }, + "Microsoft.WindowsAppSDK": { + "type": "Direct", + "requested": "[1.8.260508005, )", + "resolved": "1.8.260508005", + "contentHash": "+aA+zrvqJKgsn/1TPOSR0Uy7dkMO5jI/+cDWPu2pWmXe4KQxYkR8gQFY9IrfbgCxxSd/yl1zMcAKD/4HBbNqaw==", + "dependencies": { + "Microsoft.WindowsAppSDK.AI": "[1.8.76]", + "Microsoft.WindowsAppSDK.Base": "[1.8.251216001]", + "Microsoft.WindowsAppSDK.DWrite": "[1.8.25122902]", + "Microsoft.WindowsAppSDK.Foundation": "[1.8.260505001]", + "Microsoft.WindowsAppSDK.InteractiveExperiences": "[1.8.260430001]", + "Microsoft.WindowsAppSDK.ML": "[1.8.2197]", + "Microsoft.WindowsAppSDK.Runtime": "[1.8.260508005]", + "Microsoft.WindowsAppSDK.Widgets": "[1.8.251231004]", + "Microsoft.WindowsAppSDK.WinUI": "[1.8.260505002]" + } + }, + "Microsoft.Web.WebView2": { + "type": "Transitive", + "resolved": "1.0.3179.45", + "contentHash": "3pokSH5CnN0G6rGhGFo1y87inxYhNxBQ2Vdf0wlvBj99KHxQJormjDACmqRnFeUsmuNFIhWwfAL1ztq7wD5qRA==" + }, + "Microsoft.Windows.SDK.BuildTools": { + "type": "Transitive", + "resolved": "10.0.26100.4654", + "contentHash": "2mgcOlj/t2RfSyyw+pVESfO+Tk1RkfQzto9Vrq42M1lUQIfQEwbi8QLha9GXWIOj+TFzeHIEJckIoF25mgiM8A==" + }, + "Microsoft.Windows.SDK.BuildTools.MSIX": { + "type": "Transitive", + "resolved": "1.7.20250829.1", + "contentHash": "IMdvRmCIZnBS5GkYnv0po1bcx6U1OF39pqA4TphQ9evDzpCRoSE19/PkDvlUNNrBavTsLIEJgd/TAIFner75ow==" + }, + "Microsoft.WindowsAppSDK.AI": { + "type": "Transitive", + "resolved": "1.8.76", + "contentHash": "Ayn9QybcwzH+c8eQlE7dm2oO3Jrcn2uohLcsHJpCbLOAfcisjzwtSBe0oyulbaJ86R4eDSX3RDS25tsjGpIqyQ==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001", + "Microsoft.WindowsAppSDK.Foundation": "1.8.260501000" + } + }, + "Microsoft.WindowsAppSDK.Base": { + "type": "Transitive", + "resolved": "1.8.251216001", + "contentHash": "PS1wriuFknz3W2F2P/e6RvOTM35w89Lsj/f0QmUEPrJjKnc+jM0JLX1vfdytI14y1gNRUTm9uclwP0aH/SVU5w==", + "dependencies": { + "Microsoft.Windows.SDK.BuildTools": "10.0.26100.4654", + "Microsoft.Windows.SDK.BuildTools.MSIX": "1.7.20250829.1" + } + }, + "Microsoft.WindowsAppSDK.DWrite": { + "type": "Transitive", + "resolved": "1.8.25122902", + "contentHash": "zFNn07i7Cyz62Y8FnPQAyzeZK7ww3m9t42i9pzy4C04pNbyUDQ4fG7pB6VSh6n4EyFuYtuFQuDzt4mKmXFrkrg==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001" + } + }, + "Microsoft.WindowsAppSDK.Foundation": { + "type": "Transitive", + "resolved": "1.8.260505001", + "contentHash": "41SSoEn3sKKCAVPA/w18zVJwV1C7aDEkPS2f6Zyp19m27tDEmteEp0XS3Ln3b0ElYR4FfPEPvIDbJPSA9vePGw==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001", + "Microsoft.WindowsAppSDK.InteractiveExperiences": "1.8.260430001" + } + }, + "Microsoft.WindowsAppSDK.InteractiveExperiences": { + "type": "Transitive", + "resolved": "1.8.260430001", + "contentHash": "fTPCnQb3ZarMh9khlEfbLDllcZzK0tSP+2S6W/T4cPyLLSAmARm8Gd3AC5kqubnDjzTvG+1lNZ1bZGFsIHplJQ==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001" + } + }, + "Microsoft.WindowsAppSDK.ML": { + "type": "Transitive", + "resolved": "1.8.2197", + "contentHash": "6Bc1SOLd5HicY3GbF+zr76YBfH4iZOKeEGxTK/lHKAK2ExZTWavOADxB2CKSi/irF4dWSngUdRFopWPmckJ6fA==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001", + "Microsoft.WindowsAppSDK.Foundation": "1.8.260505001" + } + }, + "Microsoft.WindowsAppSDK.Runtime": { + "type": "Transitive", + "resolved": "1.8.260508005", + "contentHash": "2JqXzA4heHSkkaXJNyEvYtpv2wsEUMhwQzgXmZsFmYSkUXSdeVc6hdHcWoBK1SY9l1Sjd1brLt7h16kIpgh2kA==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001" + } + }, + "Microsoft.WindowsAppSDK.Widgets": { + "type": "Transitive", + "resolved": "1.8.251231004", + "contentHash": "bIWqQYR8DCoB1SoPOMil5AtgtkTn438wJTdpsHgyO/6o7Eh7PMP5BzrR0KbDsFqy+4LhPWQ4vtwko5k93fECcA==", + "dependencies": { + "Microsoft.WindowsAppSDK.Base": "1.8.251216001" + } + }, + "Microsoft.WindowsAppSDK.WinUI": { + "type": "Transitive", + "resolved": "1.8.260505002", + "contentHash": "/hGl6EOmo8aeJR2bYbOmGhOicnJK4EY+u+cQI+jm8H1uXThfMX32DsfKOt9LkOcu8AKX3j5FpiBay8ObUSytIw==", + "dependencies": { + "Microsoft.Web.WebView2": "1.0.3179.45", + "Microsoft.WindowsAppSDK.Base": "1.8.251216001", + "Microsoft.WindowsAppSDK.Foundation": "1.8.260505001", + "Microsoft.WindowsAppSDK.InteractiveExperiences": "1.8.260430001" + } + } + } + } +} diff --git a/packages/components/Callout/windows/Callout/pch.cpp b/packages/components/Callout/windows/Callout/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/packages/components/Callout/windows/Callout/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/packages/components/Callout/windows/Callout/pch.h b/packages/components/Callout/windows/Callout/pch.h new file mode 100644 index 0000000000..4eac9041ed --- /dev/null +++ b/packages/components/Callout/windows/Callout/pch.h @@ -0,0 +1,28 @@ +// pch.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#define NOMINMAX 1 +//#define WIN32_LEAN_AND_MEAN 1 +//#define WINRT_LEAN_AND_MEAN 1 + +// Windows Header Files +#include +#undef GetCurrentTime +#include + +// WinRT Header Files +#include +#include +#include + +// C RunTime Header Files +#include +#include +#include +#include + +// Reference additional headers your project requires here diff --git a/packages/components/Callout/windows/NuGet.Config b/packages/components/Callout/windows/NuGet.Config new file mode 100644 index 0000000000..aee3137fd7 --- /dev/null +++ b/packages/components/Callout/windows/NuGet.Config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/yarn.lock b/yarn.lock index 660957ab8e..a0ae34bc95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3035,6 +3035,7 @@ __metadata: resolution: "@fluentui-react-native/components@workspace:packages/agentic-components" dependencies: "@babel/core": "catalog:" + "@fluentui-react-native/callout": "workspace:*" "@fluentui-react-native/design": "workspace:*" "@fluentui-react-native/framework-base": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" @@ -3051,6 +3052,7 @@ __metadata: react-native-svg: "npm:^15.12.1" test-renderer: "catalog:0.81" peerDependencies: + "@office-iss/react-native-win32": ^0.74.0 || ^0.81.0 "@types/react": ~18.2.0 || ~19.0.0 || ~19.1.4 react: 18.2.0 || 19.0.0 || 19.1.4 react-native: ^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.6 @@ -3058,6 +3060,8 @@ __metadata: react-native-svg: ">=15.0.0 <15.13.0 || >=15.4.0 <15.13.0 || ^15.11.2 || ^15.12.1" react-native-windows: ^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0 peerDependenciesMeta: + "@office-iss/react-native-win32": + optional: true "@types/react": optional: true react-native-macos: