Skip to content

Xaml support prototype #14811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions packages/sample-app-fabric/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @format
*/

import React from 'react';
import React, {useState} from 'react';
import type {PropsWithChildren} from 'react';
import {
SafeAreaView,
Expand All @@ -25,6 +25,8 @@ import {
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

import {CalendarView} from 'xaml-calendar-view';

type SectionProps = PropsWithChildren<{
title: string;
}>;
Expand Down Expand Up @@ -62,6 +64,8 @@ function App(): React.JSX.Element {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};

const [selectedDate, setSelectedDate] = useState(true);

return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
Expand All @@ -77,9 +81,20 @@ function App(): React.JSX.Element {
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
Edit <Text style={styles.highlight}>App.tsx</Text> to change this.
I'll try showing a CalendarView here (displayMode=1 for month view).
</Section>

<CalendarView
style={{width: 400, height: 400}}
displayMode="1"
onSelectedDatesChanged={e => {
setSelectedDate(e.nativeEvent.startDate);
}}
/>

<Section title="Selected date">Selected date: {selectedDate}</Section>

<Section title="See Your Changes">
<ReloadInstructions />
</Section>
Expand Down
14 changes: 14 additions & 0 deletions packages/sample-app-fabric/b.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
setlocal
pushd ..\..

call yarn || goto :fail

popd

call yarn react-native run-windows --msbuildprops RestoreForceEvaluate=true || goto :fail

echo Success!
exit /b 0

echo FAILED.
exit /b 1
5 changes: 3 additions & 2 deletions packages/sample-app-fabric/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"@typescript-eslint/parser": "^7.1.1",
"react": "^19.0.0",
"react-native": "0.79.0-nightly-20250303-cee63397b",
"react-native-windows": "^0.0.0-canary.964"
"react-native-windows": "^0.0.0-canary.964",
"xaml-calendar-view": ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version should be more concrete.

},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand Down Expand Up @@ -48,4 +49,4 @@
"engines": {
"node": ">=18"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@
"boost": "[1.83.0, )"
}
},
"microsoft.reactnative.xaml": {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this entry is here -- "microsoft.reactnative.xaml" isn't a package, is it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lock files include project dependencies

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this should go away when we remove the m.rn.xaml.vcxproj then.

"type": "Project",
"dependencies": {
"Common": "[1.0.0, )",
"Folly": "[1.0.0, )",
"Microsoft.ReactNative": "[1.0.0, )",
"Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"ReactCommon": "[1.0.0, )",
"boost": "[1.83.0, )"
}
},
"reactcommon": {
"type": "Project",
"dependencies": {
Expand All @@ -95,6 +106,18 @@
"dependencies": {
"Microsoft.JavaScript.Hermes": "[0.0.0-2505.2001-0e4bc3b9, )",
"Microsoft.ReactNative": "[1.0.0, )",
"Microsoft.ReactNative.Xaml": "[1.0.0, )",
"Microsoft.VCRTForwarders.140": "[1.0.2-rc, )",
"Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"XamlCalendarView": "[1.0.0, )",
"boost": "[1.83.0, )"
}
},
"xamlcalendarview": {
"type": "Project",
"dependencies": {
"Microsoft.ReactNative": "[1.0.0, )",
"Microsoft.ReactNative.Xaml": "[1.0.0, )",
"Microsoft.VCRTForwarders.140": "[1.0.2-rc, )",
"Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"boost": "[1.83.0, )"
Expand Down
27 changes: 27 additions & 0 deletions packages/sample-app-fabric/windows/SampleAppFabric.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\..\..\vnext\Mso\M
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\..\..\vnext\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XamlCalendarView", "..\..\..\node_modules\xaml-calendar-view\windows\XamlCalendarView\XamlCalendarView.vcxproj", "{3501592F-3196-4EE1-B4AA-FE4A5F29063B}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\..\..\vnext\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9
Expand Down Expand Up @@ -154,6 +156,31 @@ Global
{14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.ActiveCfg = Release|Win32
{14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Build.0 = Release|Win32
{14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Deploy.0 = Release|Win32

{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Debug|x64.ActiveCfg = Debug|x64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Debug|x64.Build.0 = Debug|x64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Debug|x86.ActiveCfg = Debug|Win32
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Debug|x86.Build.0 = Debug|Win32
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Debug|ARM64.Build.0 = Debug|ARM64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Release|x64.ActiveCfg = Release|x64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Release|x64.Build.0 = Release|x64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Release|x86.ActiveCfg = Release|Win32
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Release|x86.Build.0 = Release|Win32
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Release|ARM64.ActiveCfg = Release|ARM64
{E5D1B5D2-0E6A-4011-8BCD-08968256DCBD}.Release|ARM64.Build.0 = Release|ARM64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Debug|x64.ActiveCfg = Debug|x64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Debug|x64.Build.0 = Debug|x64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Debug|x86.ActiveCfg = Debug|Win32
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Debug|x86.Build.0 = Debug|Win32
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Debug|ARM64.ActiveCfg = Debug|ARM64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Debug|ARM64.Build.0 = Debug|ARM64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Release|x64.ActiveCfg = Release|x64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Release|x64.Build.0 = Release|x64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Release|x86.ActiveCfg = Release|Win32
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Release|x86.Build.0 = Release|Win32
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Release|ARM64.ActiveCfg = Release|ARM64
{3501592F-3196-4EE1-B4AA-FE4A5F29063B}.Release|ARM64.Build.0 = Release|ARM64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
#include "pch.h"
#include "AutolinkedNativeModules.g.h"

// Includes from xaml-calendar-view
#include <winrt/XamlCalendarView.h>

namespace winrt::Microsoft::ReactNative
{

void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::ReactNative::IReactPackageProvider> const& packageProviders)
{
UNREFERENCED_PARAMETER(packageProviders);
// IReactPackageProviders from xaml-calendar-view
packageProviders.Append(winrt::XamlCalendarView::ReactPackageProvider());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- AutolinkedNativeModules.g.targets contents generated by "npx @react-native-community/cli autolink-windows" -->
<ItemGroup>
<!-- Projects from xaml-calendar-view -->
<ProjectReference Include="$(ProjectDir)..\..\..\..\node_modules\xaml-calendar-view\windows\XamlCalendarView\XamlCalendarView.vcxproj">
<Project>{3501592F-3196-4EE1-B4AA-FE4A5F29063B}</Project>
</ProjectReference>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "NativeModules.h"

#include <winrt/Microsoft.ReactNative.Xaml.h>

// A PackageProvider containing any turbo modules you define within this app project
struct CompReactPackageProvider
: winrt::implements<CompReactPackageProvider, winrt::Microsoft::ReactNative::IReactPackageProvider> {
Expand Down Expand Up @@ -39,6 +41,9 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR
RegisterAutolinkedNativeModulePackages(settings.PackageProviders());
// Register any native modules defined within this app project
settings.PackageProviders().Append(winrt::make<CompReactPackageProvider>());
// TODO: Can we make apps register this automatically? (e.g. with autolinking)
// TODO: But ideally we don't load the M.RN.Xaml.dll at all if we're not using Xaml.
settings.PackageProviders().Append(winrt::Microsoft::ReactNative::Xaml::ReactPackageProvider());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: I don't know how to make this automatically happen with autolinking or whatever. Can the xaml-calendar-view package do this somehow? OTOH, if the app's not using Xaml, I don't want it to load Microsoft.ReactNative.Xaml.dll. Not sure the best way to handle this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would probably want to update autolinking to conditionally add the winrt::Microsoft::ReactNative::Xaml::ReactPackageProvider() to be loaded in the generated AutoLinkedNativeModules.g.cpp file.

We can either detect it needs to be there during autolinking (modules might register that they need it) and inject it there, OR we could just always put it in, behind a #ifdef RNW_NEW_ARCH_XAML_SUPPORT, powered by an aforementioned RnwNewArchXamlSupport variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another solution: if XamlHost were in its own package, the existing autolinking logic should make this work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DECISION: We decided to remove the Microsoft.ReactNative.Xaml.dll and move XamlHost into Microsoft.ReactNative.dll. This should resolve this problem.


#if BUNDLE
// Load the JS bundle from a file (not Metro):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,33 @@
"boost": "[1.83.0, )"
}
},
"microsoft.reactnative.xaml": {
"type": "Project",
"dependencies": {
"Common": "[1.0.0, )",
"Folly": "[1.0.0, )",
"Microsoft.ReactNative": "[1.0.0, )",
"Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"ReactCommon": "[1.0.0, )",
"boost": "[1.83.0, )"
}
},
"reactcommon": {
"type": "Project",
"dependencies": {
"Folly": "[1.0.0, )",
"boost": "[1.83.0, )"
}
},
"xamlcalendarview": {
"type": "Project",
"dependencies": {
"Microsoft.ReactNative": "[1.0.0, )",
"Microsoft.ReactNative.Xaml": "[1.0.0, )",
"Microsoft.VCRTForwarders.140": "[1.0.2-rc, )",
"Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"boost": "[1.83.0, )"
}
}
},
"native,Version=v0.0/win": {
Expand Down
1 change: 1 addition & 0 deletions packages/sample-app-fabric/windows/SampleAppFabric/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <CppWinRTIncludes.h>
#include <winrt/Microsoft.ReactNative.Composition.h>
#include <winrt/Microsoft.ReactNative.Xaml.h>
#include <winrt/Microsoft.ReactNative.h>
#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Content.h>
Expand Down
1 change: 1 addition & 0 deletions packages/xaml-calendar-view/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib-commonjs/
11 changes: 11 additions & 0 deletions packages/xaml-calendar-view/NuGet.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="react-native" value="https://pkgs.dev.azure.com/ms/react-native/_packaging/react-native-public/nuget/v3/index.json" />
<add key="Nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>
3 changes: 3 additions & 0 deletions packages/xaml-calendar-view/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: ['module:@rnw-scripts/babel-react-native-config'],
};
3 changes: 3 additions & 0 deletions packages/xaml-calendar-view/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
preset: 'react-native',
};
51 changes: 51 additions & 0 deletions packages/xaml-calendar-view/just-task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
* @format
* @ts-check
*/

const {task, series} = require('just-scripts');
const fs = require('fs');
const glob = require('glob');
const path = require('path');

// Use the shared base configuration
require('@rnw-scripts/just-task');

// The TS build process will strip all the types from the NativeComponent spec file. Which means that the codegen babel will not correctly generate the JS view config for the component.
// So here we manually run babel, overwriting the tsc output, so that we ship the generated code.
task('codegenNativeComponents', () => {
const babel = require('@babel/core');
const matches = glob.sync('src/**/*NativeComponent.ts');

matches.forEach(matchedPath => {
const relativePath = path.relative(
path.resolve(process.cwd(), 'src'),
matchedPath,
);
const code = fs.readFileSync(matchedPath).toString();
const filename = path.resolve(process.cwd(), matchedPath);

const res = babel.transformSync(code, {
ast: false,
filename,
cwd: process.cwd(),
sourceRoot: process.cwd(),
root: process.cwd(),
babelrc: true,
});

const relativeOutputPath = relativePath.replace(/\.ts$/, '.js');

fs.writeFileSync(
path.resolve(process.cwd(), 'lib-commonjs', relativeOutputPath),
res?.code,
);
});
});

task(
'buildWithCodegetNativeComponents',
series('build', 'codegenNativeComponents'),
);
57 changes: 57 additions & 0 deletions packages/xaml-calendar-view/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "xaml-calendar-view",
"version": "1.0.0",
"main": "lib-commonjs/index.js",
"types": "lib-commonjs/index.d.ts",
"license": "MIT",
"private": true,
"scripts": {
"build": "rnw-scripts buildWithCodegetNativeComponents",
"lint": "rnw-scripts lint",
"lint:fix": "rnw-scripts lint:fix"
},
"codegenConfig": {
"name": "CalendarView",
"type": "all",
"jsSrcsDir": "src",
"windows": {
"generators": "componentsWindows",
"namespace": "winrt::XamlCalendarView::Codegen",
"outputDirectory": "windows/XamlCalendarView/codegen"
}
},
"dependencies": {
"@types/react": "^19.0.0",
"react": "^19.0.0",
"react-native": "0.79.0-nightly-20250123-d1028885e",
"react-native-windows": "^0.0.0-canary.952"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/generator": "^7.25.0",
"@babel/preset-env": "^7.25.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.20.0",
"@react-native-community/cli": "15.0.0-alpha.2",
"@react-native/metro-config": "0.79.0-nightly-20250123-d1028885e",
"@rnw-scripts/babel-node-config": "2.3.2",
"@rnw-scripts/babel-react-native-config": "0.0.0",
"@rnw-scripts/eslint-config": "1.2.34",
"@rnw-scripts/just-task": "2.3.51",
"@rnw-scripts/metro-dev-config": "0.0.0",
"@rnw-scripts/ts-config": "2.0.5",
"@rnx-kit/jest-preset": "^0.1.16",
"@types/react": "^19.0.0",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"babel-jest": "^29.6.3",
"eslint": "^8.19.0",
"glob": "^7.1.6",
"just-scripts": "^1.3.3",
"prettier": "2.8.8",
"typescript": "5.0.4"
},
"engines": {
"node": ">=18"
}
}
15 changes: 15 additions & 0 deletions packages/xaml-calendar-view/src/CalendarView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import * as React from 'react';
import RawCalendarView from './CalendarViewNativeComponent';

import XamlHost from 'react-native-windows/Libraries/Components/Xaml/XamlHost';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an awkward import into a deep path. Should XamlHost be a separate package?


function CalendarView(props: any) {
return (
<XamlHost label='unused'>
<RawCalendarView label='unused' {...props} />
</XamlHost>
);
}

export { CalendarView, RawCalendarView };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is that the CalendarView object is already wrapped in a XamlHost, so is all ready to be put into a JSX tree. RawCalendarView is not. In the future maybe we make it possible to group multiple "Raw" elements under the same XamlHost.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you had a tree of Xaml elements, would you expect something like this? (maybe not supporting layout panels with multiple children, but other controls that have 1 child)

<XamlHost>
  <RawStackPanel>
    <RawButton />
  </RawStackPanel>
</XamlHost>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could support this without too much work if we want to. I haven't spent a lot of time exploring that though.

Loading