Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changeset/modern-native-core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fluentui-react-native/native-core": minor
---

Initialize the native-core package with cross-platform, macOS, Windows, shared Windows/Win32, and Win32 entrypoints and a native component and TurboModule organization plan.

Own the Callout and FocusZone JS wrappers, codegen specifications, macOS Paper/Fabric adapters, and Windows Fabric implementations in this shared package.
9 changes: 9 additions & 0 deletions .changeset/native-core-component-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@fluentui-react-native/callout": patch
"@fluentui-react-native/focus-zone": patch
"@fluentui-react-native/components": patch
"@fluentui-react-native/storybook-desktop-runtime": patch
"@fluentui-react-native/tester": patch
---

Move Callout and FocusZone implementations into native-core and retain the existing packages as JS-only compatibility shims under packages/shim. Native applications must directly depend on native-core and regenerate native autolinking/Pods; macOS now uses the shared FRNNativeCore pod and Windows uses one Fabric library for both components. Public APIs and native registration names are unchanged.
9 changes: 9 additions & 0 deletions .changeset/native-core-legacy-entrypoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@fluentui-react-native/native-core": minor
"@fluentui-react-native/callout": patch
"@fluentui-react-native/focus-zone": patch
"@fluentui-react-native/components": patch
"@fluentui-react-native/storybook-desktop-runtime": patch
---

Isolate the existing Callout and FocusZone JavaScript wrappers under native-core/legacy instead of the package root. Compatibility shims retain their exports through this submodule; native implementations and codegen specifications are unchanged.
5 changes: 5 additions & 0 deletions .changeset/native-core-macos-interop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fluentui-react-native/native-core': patch
---

Fix the macOS Swift interoperability header name for the consolidated native-core pod.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ focus visual should be presented as active.
exposure or global numeric-tag lookup.

Local references:
`packages\components\FocusZone\macos\RCTFocusZone.m:24-35,122-129`;
`packages\components\FocusZone\macos\RCTFocusZoneComponentView.mm:118-179`.
`packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129`;
`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-179`.
Installed RNmacOS `React\Fabric\Mounting\ComponentViews\View\RCTViewComponentView.mm:1719-1804`
implements focus/blur commands and responder event emission.

Expand Down Expand Up @@ -105,8 +105,8 @@ same keys. Recompute eligibility when children mount/unmount and when props
change, and clear the default responder on recycle. Retain direction, wrap/stop,
RTL, nested zones, scrolling, and editable-child semantics.

Evidence: `packages\components\FocusZone\macos\RCTFocusZone.m:584-675`;
`packages\components\FocusZone\macos\RCTFocusZoneComponentView.mm:118-195`.
Evidence: `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:584-675`;
`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195`.

## Popup windows and lifecycle

Expand All @@ -131,10 +131,10 @@ Do not install OS-global keyboard hooks or require desktop-driver permissions
for ordinary production focus behavior.

Evidence:
`packages\native\Callout\macos\CalloutWindow.swift:9-38`;
`packages\native\Callout\macos\CalloutView.swift:33-45,142-216`;
`packages\native\Callout\macos\RCTCalloutComponentView.mm:96-132`;
`packages\native\Callout\macos\GuardedEventMonitor.swift:1-29`;
`packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`;
`packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`;
`packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`;
`packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29`;
`packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55`.

## Native versus custom ring drawing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ assume a React ref can be serialized into a TurboModule argument.

The existing Fabric FocusZone uses `TryFocus(FocusState::Keyboard)` and checks
the root's focused component after the request
(`packages\components\FocusZone\windows\FRNFocusZone\FocusZoneComponentView.cpp:175-224`).
(`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:175-224`).
Reuse this request-versus-observation pattern. Do not report success merely
because JavaScript called an optional `.focus()` method.

Expand Down Expand Up @@ -94,7 +94,7 @@ This native implementation detail is not a promised public JavaScript API.
consume every key.

Sources: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`;
`packages\components\FocusZone\windows\FRNFocusZone\FocusZoneComponentView.cpp:71-133`.
`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:71-133`.

## Native and custom focus visuals

Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ apps/*/.vscode/.react/
# Visual Studio
.vs/

# Native library outputs, including caches left behind by workspace relocations
packages/**/windows/**/[Oo]bj/
packages/**/windows/**/Generated Files/
packages/**/windows/**/x64/[Dd]ebug*/
packages/**/windows/**/x64/[Rr]elease*/

# Ignore Gradle build output directory
**/android/**/build

Expand Down
2 changes: 1 addition & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"**/*.generated.*",
"**/lib-commonjs/**",
"**/dist/**",
"packages/native/Callout/windows/Callout/codegen/**",
"packages/agentic/native-core/windows/NativeCore/codegen/**",
"**/CHANGELOG.*",
"**/CODE_OF_CONDUCT.md",
"**/SECURITY.md",
Expand Down
24 changes: 16 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ This is the **FluentUI React Native** repository, a monorepo containing React Na
/win32/ - Win32-specific test app
/component-generator/ - Tool to generate new components
/packages/ - Core library packages
/agentic/native-core/ - Shared modern native components and TurboModules
/components/ - UI component implementations (Button, Checkbox, Avatar, etc.)
/native/ - Standalone native-backed component packages (Callout, etc.)
/shim/ - Compatibility entrypoints (Callout, FocusZone, theming, etc.)
/framework/ - Core theming and composition framework
/composition/ - Component composition factory (current approach)
/theme/ - Theme system
Expand Down Expand Up @@ -237,8 +238,9 @@ The composition framework uses precise types for better type safety:

### Component Development

**Component Location**: Stable JavaScript components are in `/packages/components/`, standalone native-backed components are in
`/packages/native/`, and components under development are in `/packages/experimental/`.
**Component Location**: Stable JavaScript components are in `/packages/components/`, components under development are in
`/packages/experimental/`, and compatibility packages are in `/packages/shim/`. Modern native components and modules share
`/packages/agentic/native-core/`; follow its [organization plan](packages/agentic/native-core/README.md).

**Component Structure**: Each component typically has:

Expand Down Expand Up @@ -272,19 +274,25 @@ The composition framework uses precise types for better type safety:

- **Do not use barrel exports (`export * from '...'`)** - wildcard re-exports break tree-shaking because bundlers cannot statically determine which symbols are used, so unused code is retained in consumers' bundles. Always use explicit named re-exports instead, e.g. `export { Foo, Bar } from './module'` and `export type { Baz } from './module'`.

**Native Modules**: Standalone native-backed component packages, such as Callout, live in `/packages/native/`. Components with
native code (iOS/Android/Windows):
**Native Modules**: Callout and FocusZone implementations live in `@fluentui-react-native/native-core`; their former packages
are JS-only compatibility shims under `/packages/shim/`. Their JavaScript wrappers live in `src/legacy` and are exported
only from `@fluentui-react-native/native-core/legacy`, not native-core's root entrypoint. Components with native code:

- Typically have one root slot wrapping the native component
- Use `codegenNativeComponent` for new architecture compatibility
- May use `constantsToExport` for default values from native side
- iOS/macOS: Include `.podspec` files
- Must be added to FluentTester's Podfile (transitive dependencies aren't autolinked)
- Native applications must directly depend on native-core for native project discovery and autolinking

For new modern native components and modules, use `@fluentui-react-native/native-core` rather than creating another standalone
package. Its Windows components are Fabric-only; macOS components support Paper and Fabric initially. Export shared APIs
from the root and scope platform APIs to `macos`, `windows` (React Native Windows), `win` (Windows/Win32), or `win32`.
Callout and FocusZone retain their existing public APIs and native registration names after migration.

### Creating a New Component

1. Create the package under `/packages/components/<ComponentName>`, `/packages/native/<ComponentName>` for a standalone
native-backed component, or `/packages/experimental/<ComponentName>` while it is under development
1. Create the package under `/packages/components/<ComponentName>` or `/packages/experimental/<ComponentName>` while it is
under development. Add modern native components/modules to the existing `/packages/agentic/native-core/` package instead.
2. Copy structure from existing component (e.g., Shimmer, Button)
3. Update `package.json` with correct name and dependencies (use `workspace:*` for internal packages)
4. Add the new package's `tsconfig.json` to the root `tsconfig.json` `references` so it joins the unified build
Expand Down
2 changes: 1 addition & 1 deletion apps/E2E/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"include": ["src"],
"references": [
{
"path": "../../packages/components/FocusZone/tsconfig.json"
"path": "../../packages/shim/focus-zone/tsconfig.json"
},
{
"path": "../../scripts/tsconfig.json"
Expand Down
1 change: 1 addition & 0 deletions apps/fluent-tester/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"windows": "react-native run-windows --arch x64 --sln windows/FluentTester.sln"
},
"dependencies": {
"@fluentui-react-native/native-core": "workspace:*",
"@types/node": "catalog:",
"@types/react": "~19.1.4",
"react": "19.1.4",
Expand Down
7 changes: 5 additions & 2 deletions apps/fluent-tester/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"include": ["src"],
"references": [
{
"path": "../../packages/native/Callout/tsconfig.json"
"path": "../../packages/shim/callout/tsconfig.json"
},
{
"path": "../../packages/experimental/AppearanceAdditions/tsconfig.json"
Expand All @@ -32,11 +32,14 @@
"path": "../../packages/experimental/Shimmer/tsconfig.json"
},
{
"path": "../../packages/components/FocusZone/tsconfig.json"
"path": "../../packages/shim/focus-zone/tsconfig.json"
},
{
"path": "../../packages/components/MenuButton/tsconfig.json"
},
{
"path": "../../packages/agentic/native-core/tsconfig.json"
},
{
"path": "../../packages/components/RadioGroup/tsconfig.json"
},
Expand Down
15 changes: 8 additions & 7 deletions apps/storybook/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Agentic Components Storybook

On-device [Storybook](https://storybook.js.org/) test app (Storybook for React Native v10) for
`@fluentui-react-native/components` and linked standalone native packages. It loads every
`*.stories.(ts|tsx)` file from the agentic components package plus the standalone Callout
package. FocusZone stories are agentic primitive stories, while its standalone native package
remains linked so they run in the Fabric and Paper hosts.
`@fluentui-react-native/components` and `@fluentui-react-native/native-core`. It loads every
`*.stories.(ts|tsx)` file from the agentic components package plus native-core's Callout
stories. FocusZone stories remain agentic primitive stories and use native-core's
`legacy` entrypoint, as does the Win32 Callout portal.
Native-core owns the shared macOS pod and Windows Fabric library for both components.

The reusable desktop CLI and configuration live in
`packages/agentic/storybook-desktop`, with peer-dependent React Native
Expand Down Expand Up @@ -129,9 +130,9 @@ by smoke so its bundle identity does not fall back to the generated app default.
## Running on Windows

The Windows app also uses `react-native-test-app`. Its generated Win32 project uses React Native
Windows 0.81's New Architecture and Fabric renderer. The Callout and FocusZone packages are
autolinked as Windows Fabric native libraries; their Paper implementations remain available on
their supported legacy endpoints.
Windows 0.81's New Architecture and Fabric renderer. Native-core is autolinked as one Windows
Fabric native library that registers Callout and FocusZone. macOS retains both Paper and Fabric
adapters; Win32 continues using its host-provided native implementations.

```powershell
# from this directory
Expand Down
2 changes: 1 addition & 1 deletion apps/storybook/agent-map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ sources:
policy: generated-do-not-edit
story_packages:
- '@fluentui-react-native/components'
- '@fluentui-react-native/callout'
- '@fluentui-react-native/native-core'

services:
supervisor:
Expand Down
6 changes: 2 additions & 4 deletions apps/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
"format": "fluentui-scripts format"
},
"dependencies": {
"@fluentui-react-native/callout": "workspace:*",
"@fluentui-react-native/components": "workspace:*",
"@fluentui-react-native/focus-zone": "workspace:*",
"@fluentui-react-native/native-core": "workspace:*",
"@fluentui-react-native/storybook-desktop": "workspace:*",
"@fluentui-react-native/storybook-desktop-runtime": "workspace:*",
"@office-iss/react-native-win32": "^0.81.0",
Expand Down Expand Up @@ -57,9 +56,8 @@
"furn": {
"knip": {
"ignoreDependencies": [
"@fluentui-react-native/callout",
"@fluentui-react-native/components",
"@fluentui-react-native/focus-zone",
"@fluentui-react-native/native-core",
"react-native-svg"
]
}
Expand Down
2 changes: 1 addition & 1 deletion apps/storybook/storybook.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default makeDesktopStorybookConfig({
},
],
[
'@fluentui-react-native/callout',
'@fluentui-react-native/native-core',
{
platformSettings: {
windows: {
Expand Down
11 changes: 4 additions & 7 deletions apps/storybook/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,13 @@
"exclude": ["node_modules", "dist", ".cache"],
"references": [
{
"path": "../../packages/agentic/storybook-desktop/tsconfig.json"
},
{
"path": "../../packages/native/Callout/tsconfig.json"
"path": "../../packages/agentic/components/tsconfig.json"
},
{
"path": "../../packages/agentic/components/tsconfig.json"
"path": "../../packages/agentic/desktop-driver/tsconfig.json"
},
{
"path": "../../packages/components/FocusZone/tsconfig.json"
"path": "../../packages/agentic/native-core/tsconfig.json"
},
{
"path": "../../scripts/tsconfig.json"
Expand All @@ -34,7 +31,7 @@
"path": "../../packages/agentic/storybook-desktop-runtime/tsconfig.json"
},
{
"path": "../../packages/agentic/desktop-driver/tsconfig.json"
"path": "../../packages/agentic/storybook-desktop/tsconfig.json"
}
]
}
4 changes: 2 additions & 2 deletions apps/tester-core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"path": "../../packages/components/Button/tsconfig.json"
},
{
"path": "../../packages/native/Callout/tsconfig.json"
"path": "../../packages/shim/callout/tsconfig.json"
},
{
"path": "../../packages/components/Chip/tsconfig.json"
Expand Down Expand Up @@ -83,7 +83,7 @@
"path": "../../packages/experimental/Shimmer/tsconfig.json"
},
{
"path": "../../packages/components/FocusZone/tsconfig.json"
"path": "../../packages/shim/focus-zone/tsconfig.json"
},
{
"path": "../../packages/framework-base/tsconfig.json"
Expand Down
18 changes: 9 additions & 9 deletions packages/agentic/components/WIN32-FOCUS-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ paths. The installed Win32 JavaScript inspected was
`packages\components\Menu\src\MenuPopover\useMenuPopover.ts:42-84,100-118`.
- **V6:** `packages\components\TabList\src\Tab\useTab.win32.ts:64-110,142-152`;
`packages\components\TabList\src\TabList\useTabList.ts:142-176,183-200`.
- **V7:** `packages\components\FocusZone\src\FocusZone.tsx:15-45`;
- **V7:** `packages\agentic\native-core\src\legacy\focus-zone\FocusZone.tsx:15-45`;
`packages\components\FocusTrapZone\src\FocusTrapZone.ts:15-31`.
These wrappers do not by themselves prove native trap/restoration semantics.
- **V8:** `apps\tester-core\src\TestComponents\FocusZone\FocusZoneE2ETest.tsx:1-120`;
Expand Down Expand Up @@ -297,7 +297,7 @@ paths. The installed Win32 JavaScript inspected was

### Added Fabric, macOS, and modern-ref evidence

- **W1:** `packages\components\FocusZone\windows\FRNFocusZone\FocusZoneComponentView.cpp:33-65,71-133,175-224`
- **W1:** `packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:33-65,71-133,175-224`
subscribes to native focus/key events, requests `TryFocus(FocusState::Keyboard)`,
and checks actual focused identity. This is a Fabric implementation model for
V1 outcomes, not a reason to copy its transport to Win32.
Expand All @@ -316,18 +316,18 @@ paths. The installed Win32 JavaScript inspected was
implements AppKit focus/blur, ring-mask drawing, responder events, and pointer
focus behavior. Lines 1807-1861 emit key events and use native handled-key
entries to decide whether AppKit receives `super` handling.
- **M3:** `packages\components\FocusZone\macos\RCTFocusZone.m:24-35,122-129,584-675`
- **M3:** `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129,584-675`
resolves editable focus targets and key-view navigation, including a hierarchy
fallback for Fabric-backed zones.
`packages\components\FocusZone\macos\RCTFocusZoneComponentView.mm:118-195`
`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195`
forwards focus to its content rather than becoming a duplicate tab stop and
clears default-responder state on recycle.
- **M4:** `packages\native\Callout\macos\CalloutWindow.swift:9-38`;
`packages\native\Callout\macos\CalloutView.swift:33-45,142-216`;
`packages\native\Callout\macos\RCTCalloutComponentView.mm:96-132`.
- **M4:** `packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`;
`packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`;
`packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`.
Popup key-window activation, initial child focus, and a separate Fabric touch
attachment are distinct concerns.
`packages\native\Callout\macos\GuardedEventMonitor.swift:1-29` owns monitor cleanup.
`packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29` owns monitor cleanup.
- **M5:** `packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55`
contains a macOS layout-effect/timer workaround: a readiness problem to solve,
not a general timer recipe.
Expand All @@ -341,7 +341,7 @@ paths. The installed Win32 JavaScript inspected was
covers stable native refs and compatible replacements.
- **R2:** `packages\agentic\components\src\refs.types.test.tsx:30-84` distinguishes
structural and pressable root refs.
`packages\native\Callout\src\CalloutNativeComponent.ts:63-70` demonstrates
`packages\agentic\native-core\src\specs\components\CalloutNativeComponent.ts:63-70` demonstrates
typed native commands with host refs rather than global tag arguments.

### macOS adaptation decisions
Expand Down
Loading
Loading