From b34e3f31f9c13927d0fa19ed0975a140302ed195 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Date: Tue, 3 Feb 2026 22:12:44 -0300 Subject: [PATCH] Move WindowsManager to its own package --- .../electron/modules/WindowManager/main.ts | 7 +- .../macos/ExpoMenuBar-macOS/AppDelegate.swift | 6 ++ .../ExpoMenuBar-macOS-Bridging-Header.h | 3 +- .../ExpoMenuBar-macOS/PopoverManager.swift | 2 +- .../macos/ExpoMenuBar-macOS/WindowManager.h | 5 -- .../macos/ExpoMenuBar-macOS/WindowManager.m | 46 ------------ .../macos/ExpoMenuBar-macOS/WindowNavigator.h | 12 --- .../WindowWithDeallocCallback.h | 9 --- .../WindowWithDeallocCallback.m | 12 --- .../ExpoMenuBar.xcodeproj/project.pbxproj | 18 ----- apps/menu-bar/macos/Podfile.lock | 34 +++++++++ apps/menu-bar/package.json | 1 + apps/menu-bar/src/windows/Onboarding.tsx | 2 +- apps/menu-bar/src/windows/index.ts | 73 +++++++++++-------- .../react-native-multi-window/.eslintrc.js | 5 ++ .../macos/RNMultiWindow.h | 5 ++ .../macos/RNMultiWindow.m | 46 ++++++++++++ .../macos/RNMultiWindowNavigator.h | 18 +++++ .../macos/RNMultiWindowNavigator.m | 49 ++++++++----- .../macos/WindowWithDeallocCallback.h | 9 +++ .../macos/WindowWithDeallocCallback.m | 11 +++ .../react-native-multi-window/package.json | 38 ++++++++++ .../react-native-multi-window.podspec | 27 +++++++ .../src}/WindowProvider.tsx | 0 .../react-native-multi-window/src}/index.ts | 49 +++++++------ .../src}/index.web.ts | 17 +++-- .../react-native-multi-window/src}/types.ts | 0 .../src}/useWindowFocus.ts | 2 +- .../react-native-multi-window/tsconfig.json | 14 ++++ yarn.lock | 32 +++++++- 30 files changed, 362 insertions(+), 190 deletions(-) delete mode 100644 apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.h delete mode 100644 apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.m delete mode 100644 apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.h delete mode 100644 apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.h delete mode 100644 apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.m create mode 100644 packages/react-native-multi-window/.eslintrc.js create mode 100644 packages/react-native-multi-window/macos/RNMultiWindow.h create mode 100644 packages/react-native-multi-window/macos/RNMultiWindow.m create mode 100644 packages/react-native-multi-window/macos/RNMultiWindowNavigator.h rename apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.m => packages/react-native-multi-window/macos/RNMultiWindowNavigator.m (75%) create mode 100644 packages/react-native-multi-window/macos/WindowWithDeallocCallback.h create mode 100644 packages/react-native-multi-window/macos/WindowWithDeallocCallback.m create mode 100644 packages/react-native-multi-window/package.json create mode 100644 packages/react-native-multi-window/react-native-multi-window.podspec rename {apps/menu-bar/src/modules/WindowManager => packages/react-native-multi-window/src}/WindowProvider.tsx (100%) rename {apps/menu-bar/src/modules/WindowManager => packages/react-native-multi-window/src}/index.ts (51%) rename {apps/menu-bar/src/modules/WindowManager => packages/react-native-multi-window/src}/index.web.ts (56%) rename {apps/menu-bar/src/modules/WindowManager => packages/react-native-multi-window/src}/types.ts (100%) rename {apps/menu-bar/src/modules/WindowManager => packages/react-native-multi-window/src}/useWindowFocus.ts (88%) create mode 100644 packages/react-native-multi-window/tsconfig.json diff --git a/apps/menu-bar/electron/modules/WindowManager/main.ts b/apps/menu-bar/electron/modules/WindowManager/main.ts index 48c0dbf9..c8f3e030 100644 --- a/apps/menu-bar/electron/modules/WindowManager/main.ts +++ b/apps/menu-bar/electron/modules/WindowManager/main.ts @@ -1,11 +1,10 @@ import { app, BrowserWindow } from 'electron'; import path from 'path'; - import { - WindowOptions, - WindowsManagerType, WindowStyleMask, -} from '../../../src/modules/WindowManager/types'; + type WindowOptions, + type WindowsManagerType, +} from 'react-native-multi-window'; const _windowsMap: { [key: string]: BrowserWindow } = {}; diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.swift b/apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.swift index 6f28b932..20888eb5 100644 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.swift +++ b/apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.swift @@ -33,6 +33,12 @@ class AppDelegate: RCTAppDelegate, NSUserNotificationCenterDelegate { let rootViewController = NSViewController() rootViewController.view = rootView + // Configure react-native-multi-window + RNMultiWindowNavigator.setRootViewFactory(self.rootViewFactory()) + if let bridge = self.bridge { + RNMultiWindowNavigator.setBridge(bridge) + } + popoverManager = PopoverManager.initializeShared(delegate: self) popoverManager.setContentViewController(rootViewController) diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/ExpoMenuBar-macOS-Bridging-Header.h b/apps/menu-bar/macos/ExpoMenuBar-macOS/ExpoMenuBar-macOS-Bridging-Header.h index 55e20085..0db88759 100644 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/ExpoMenuBar-macOS-Bridging-Header.h +++ b/apps/menu-bar/macos/ExpoMenuBar-macOS/ExpoMenuBar-macOS-Bridging-Header.h @@ -1,2 +1,3 @@ +#import + #import "DragDropStatusItemView.h" -#import "WindowNavigator.h" diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/PopoverManager.swift b/apps/menu-bar/macos/ExpoMenuBar-macOS/PopoverManager.swift index 09301f60..04aedc6f 100644 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/PopoverManager.swift +++ b/apps/menu-bar/macos/ExpoMenuBar-macOS/PopoverManager.swift @@ -98,7 +98,7 @@ class PopoverManager: NSObject { // MARK: - Actions @objc private func settingsAction() { - WindowNavigator.shared().openWindow( + RNMultiWindowNavigator.shared().openWindow( "Settings", options: [ "windowStyle": ["titlebarAppearsTransparent": true, "height": 680.0, "width": 500.0] diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.h b/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.h deleted file mode 100644 index 16287110..00000000 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.h +++ /dev/null @@ -1,5 +0,0 @@ -#import - -@interface WindowsManager : NSObject -@property (nonatomic, strong) NSMutableDictionary *windowsMap; -@end diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.m b/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.m deleted file mode 100644 index a1f90065..00000000 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowManager.m +++ /dev/null @@ -1,46 +0,0 @@ -#import -#import - -#import "WindowManager.h" -#import "WindowNavigator.h" - -@implementation WindowsManager - -RCT_EXPORT_MODULE(); - -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} - -- (NSDictionary *)constantsToExport -{ - return @{ - @"STYLE_MASK_BORDERLESS": @(NSWindowStyleMaskBorderless), - @"STYLE_MASK_TITLED": @(NSWindowStyleMaskTitled), - @"STYLE_MASK_CLOSABLE": @(NSWindowStyleMaskClosable), - @"STYLE_MASK_MINIATURIZABLE": @(NSWindowStyleMaskMiniaturizable), - @"STYLE_MASK_RESIZABLE": @(NSWindowStyleMaskResizable), - @"STYLE_MASK_UNIFIED_TITLE_AND_TOOLBAR": @(NSWindowStyleMaskUnifiedTitleAndToolbar), - @"STYLE_MASK_FULL_SCREEN": @(NSWindowStyleMaskFullScreen), - @"STYLE_MASK_FULL_SIZE_CONTENT_VIEW": @(NSWindowStyleMaskFullSizeContentView), - @"STYLE_MASK_UTILITY_WINDOW": @(NSWindowStyleMaskUtilityWindow), - @"STYLE_MASK_DOC_MODAL_WINDOW": @(NSWindowStyleMaskDocModalWindow), - @"STYLE_MASK_NONACTIVATING_PANEL": @(NSWindowStyleMaskNonactivatingPanel) - }; -} - -RCT_EXPORT_METHOD(openWindow:(NSString *)moduleName - options:(NSDictionary *)options) -{ - WindowNavigator *windowNavigator = [WindowNavigator shared]; - [windowNavigator openWindow:moduleName options:options]; -} - -RCT_EXPORT_METHOD(closeWindow:(NSString *)moduleName) -{ - WindowNavigator *windowNavigator = [WindowNavigator shared]; - [windowNavigator closeWindow:moduleName]; -} - -@end diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.h b/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.h deleted file mode 100644 index dc8f3fe1..00000000 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.h +++ /dev/null @@ -1,12 +0,0 @@ - -@interface WindowNavigator : NSObject - -@property(nonatomic, strong) NSMutableDictionary *windowsMap; - -+ (instancetype)shared; - -- (void)openWindow:(NSString *)moduleName options:(NSDictionary *)options; - -- (void)closeWindow:(NSString *)moduleName; - -@end diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.h b/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.h deleted file mode 100644 index 08b1921a..00000000 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -typedef void (^DeallocCallback)(void); - -@interface WindowWithDeallocCallback : NSWindow - -@property (nonatomic, copy) DeallocCallback deallocCallback; - -@end diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.m b/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.m deleted file mode 100644 index f1bf3564..00000000 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowWithDeallocCallback.m +++ /dev/null @@ -1,12 +0,0 @@ -#import "WindowWithDeallocCallback.h" - -@implementation WindowWithDeallocCallback - -- (void)dealloc { - // Invoke the callback block if it exists - if (self.deallocCallback) { - self.deallocCallback(); - } -} - -@end diff --git a/apps/menu-bar/macos/ExpoMenuBar.xcodeproj/project.pbxproj b/apps/menu-bar/macos/ExpoMenuBar.xcodeproj/project.pbxproj index 9e3d75d1..bf73c092 100644 --- a/apps/menu-bar/macos/ExpoMenuBar.xcodeproj/project.pbxproj +++ b/apps/menu-bar/macos/ExpoMenuBar.xcodeproj/project.pbxproj @@ -18,8 +18,6 @@ C032EDB72A1FE6F300FBC597 /* orbit-cli-arm64 in Resources */ = {isa = PBXBuildFile; fileRef = C032EDB62A1FE6F300FBC597 /* orbit-cli-arm64 */; }; C051E6B62A83577800C6D615 /* orbit-cli-x64 in Resources */ = {isa = PBXBuildFile; fileRef = C051E6B52A83577800C6D615 /* orbit-cli-x64 */; }; C051EC0E2AA227C600C6D615 /* SwifterWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C051EC0D2AA227C600C6D615 /* SwifterWrapper.swift */; }; - C0523ECC2A550983003371AF /* WindowManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C0523ECB2A550983003371AF /* WindowManager.m */; }; - C0523ED02A55980D003371AF /* WindowWithDeallocCallback.m in Sources */ = {isa = PBXBuildFile; fileRef = C0523ECF2A55980D003371AF /* WindowWithDeallocCallback.m */; }; C061C79E2A251E0C00A53D8D /* DevViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C061C79D2A251E0C00A53D8D /* DevViewController.m */; }; C061C7A12A26B21C00A53D8D /* AutoResizerRootViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C061C7A02A26B21C00A53D8D /* AutoResizerRootViewManager.m */; }; C061C7A42A26B50200A53D8D /* AutoResizerRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = C061C7A32A26B50200A53D8D /* AutoResizerRootView.m */; }; @@ -30,7 +28,6 @@ C08E652E2A5C42950079E3A9 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08E652D2A5C42950079E3A9 /* main.swift */; }; C08E65302A5C44870079E3A9 /* AutoLauncher.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = C08E651E2A5C411D0079E3A9 /* AutoLauncher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; C08E65322A5C44B90079E3A9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C08E65312A5C44B90079E3A9 /* ServiceManagement.framework */; }; - C08E65342A5D04910079E3A9 /* WindowNavigator.m in Sources */ = {isa = PBXBuildFile; fileRef = C08E65332A5D04910079E3A9 /* WindowNavigator.m */; }; C0B36EA22A65E25A004F2D8C /* Checkbox.m in Sources */ = {isa = PBXBuildFile; fileRef = C0B36E9F2A65E25A004F2D8C /* Checkbox.m */; }; C0B36EA32A65E25A004F2D8C /* CheckboxManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C0B36EA12A65E25A004F2D8C /* CheckboxManager.m */; }; C0D4407A2DDB9B9E006B7C16 /* PopoverManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D440792DDB9B9E006B7C16 /* PopoverManager.swift */; }; @@ -74,10 +71,6 @@ C051E6B52A83577800C6D615 /* orbit-cli-x64 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = "orbit-cli-x64"; path = "../../cli/orbit-cli-x64"; sourceTree = ""; }; C051EC0C2AA227C600C6D615 /* ExpoMenuBar-macOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExpoMenuBar-macOS-Bridging-Header.h"; sourceTree = ""; }; C051EC0D2AA227C600C6D615 /* SwifterWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwifterWrapper.swift; sourceTree = ""; }; - C0523ECB2A550983003371AF /* WindowManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WindowManager.m; sourceTree = ""; }; - C0523ECD2A550A04003371AF /* WindowManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WindowManager.h; sourceTree = ""; }; - C0523ECE2A5597FF003371AF /* WindowWithDeallocCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WindowWithDeallocCallback.h; sourceTree = ""; }; - C0523ECF2A55980D003371AF /* WindowWithDeallocCallback.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WindowWithDeallocCallback.m; sourceTree = ""; }; C061C79D2A251E0C00A53D8D /* DevViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DevViewController.m; sourceTree = ""; }; C061C79F2A251E3500A53D8D /* DevViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DevViewController.h; sourceTree = ""; }; C061C7A02A26B21C00A53D8D /* AutoResizerRootViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutoResizerRootViewManager.m; sourceTree = ""; }; @@ -95,8 +88,6 @@ C08E65292A5C411E0079E3A9 /* AutoLauncher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AutoLauncher.entitlements; sourceTree = ""; }; C08E652D2A5C42950079E3A9 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; C08E65312A5C44B90079E3A9 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/System/Library/Frameworks/ServiceManagement.framework; sourceTree = DEVELOPER_DIR; }; - C08E65332A5D04910079E3A9 /* WindowNavigator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WindowNavigator.m; sourceTree = ""; }; - C08E65352A5D0A580079E3A9 /* WindowNavigator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WindowNavigator.h; sourceTree = ""; }; C0B36E9E2A65E25A004F2D8C /* Checkbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Checkbox.h; sourceTree = ""; }; C0B36E9F2A65E25A004F2D8C /* Checkbox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Checkbox.m; sourceTree = ""; }; C0B36EA02A65E25A004F2D8C /* CheckboxManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CheckboxManager.h; sourceTree = ""; }; @@ -188,12 +179,6 @@ C061C7A62A26DD8C00A53D8D /* SystemIconViewManager.h */, C061C7A72A26DD9A00A53D8D /* SystemIconViewManager.m */, C061C7A92A26EDB800A53D8D /* RCTImageView+Private.h */, - C0523ECB2A550983003371AF /* WindowManager.m */, - C0523ECD2A550A04003371AF /* WindowManager.h */, - C0523ECE2A5597FF003371AF /* WindowWithDeallocCallback.h */, - C0523ECF2A55980D003371AF /* WindowWithDeallocCallback.m */, - C08E65332A5D04910079E3A9 /* WindowNavigator.m */, - C08E65352A5D0A580079E3A9 /* WindowNavigator.h */, C051EC0D2AA227C600C6D615 /* SwifterWrapper.swift */, C051EC0C2AA227C600C6D615 /* ExpoMenuBar-macOS-Bridging-Header.h */, C06B8F8F2AAB77D0009F2BB5 /* DragDropStatusItemView.m */, @@ -519,14 +504,11 @@ C0B36EA22A65E25A004F2D8C /* Checkbox.m in Sources */, C0D4407A2DDB9B9E006B7C16 /* PopoverManager.swift in Sources */, C061C7A82A26DD9A00A53D8D /* SystemIconViewManager.m in Sources */, - C0523ED02A55980D003371AF /* WindowWithDeallocCallback.m in Sources */, C06B8F902AAB77D0009F2BB5 /* DragDropStatusItemView.m in Sources */, C0B36EA32A65E25A004F2D8C /* CheckboxManager.m in Sources */, C051EC0E2AA227C600C6D615 /* SwifterWrapper.swift in Sources */, - C0523ECC2A550983003371AF /* WindowManager.m in Sources */, C0D4407C2DDBD86F006B7C16 /* AppDelegate.swift in Sources */, C061C7A42A26B50200A53D8D /* AutoResizerRootView.m in Sources */, - C08E65342A5D04910079E3A9 /* WindowNavigator.m in Sources */, C061C79E2A251E0C00A53D8D /* DevViewController.m in Sources */, C061C7A12A26B21C00A53D8D /* AutoResizerRootViewManager.m in Sources */, 77818F2A6E488E8B68D317B2 /* ExpoModulesProvider.swift in Sources */, diff --git a/apps/menu-bar/macos/Podfile.lock b/apps/menu-bar/macos/Podfile.lock index 5fc9a9bc..81897c55 100644 --- a/apps/menu-bar/macos/Podfile.lock +++ b/apps/menu-bar/macos/Podfile.lock @@ -1864,6 +1864,36 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga + - react-native-multi-window (0.0.2): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactAppDependencyProvider + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - React-NativeModulesApple (0.81.1): - boost - DoubleConversion @@ -2438,6 +2468,7 @@ DEPENDENCIES: - React-Mapbuffer (from `../../../node_modules/react-native-macos/ReactCommon`) - React-microtasksnativemodule (from `../../../node_modules/react-native-macos/ReactCommon/react/nativemodule/microtasks`) - react-native-mmkv (from `../../../node_modules/react-native-mmkv`) + - react-native-multi-window (from `../../../packages/react-native-multi-window`) - React-NativeModulesApple (from `../../../node_modules/react-native-macos/ReactCommon/react/nativemodule/core/platform/ios`) - React-oscompat (from `../../../node_modules/react-native-macos/ReactCommon/oscompat`) - React-perflogger (from `../../../node_modules/react-native-macos/ReactCommon/reactperflogger`) @@ -2594,6 +2625,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native-macos/ReactCommon/react/nativemodule/microtasks" react-native-mmkv: :path: "../../../node_modules/react-native-mmkv" + react-native-multi-window: + :path: "../../../packages/react-native-multi-window" React-NativeModulesApple: :path: "../../../node_modules/react-native-macos/ReactCommon/react/nativemodule/core/platform/ios" React-oscompat: @@ -2720,6 +2753,7 @@ SPEC CHECKSUMS: React-Mapbuffer: 15d41cf2e5215c37e0cccea3d5c4493b7bf856f2 React-microtasksnativemodule: 86204a62d73dfbd29d1e2ed93d1db2a424ade491 react-native-mmkv: df237ae1b10c6deec66787c73498b58c6264c1a6 + react-native-multi-window: 094099c56ec73a9615c730039e4930ed26f86aac React-NativeModulesApple: 7a688e1516c692afbae4ffdf3cf5d3b04cc1450d React-oscompat: 8a27536d4ae736ce0fe2c7f96c22421c31889761 React-perflogger: 269feff6b4867de2ec6fb080ccdbf52c1a2dbf0c diff --git a/apps/menu-bar/package.json b/apps/menu-bar/package.json index ff46f531..5c008494 100644 --- a/apps/menu-bar/package.json +++ b/apps/menu-bar/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "@apollo/client": "^3.8.1", + "react-native-multi-window": "*", "@expo/metro-runtime": "~6.1.2", "@expo/styleguide-native": "^1.0.1", "@fluentui/react-checkbox": "^9.2.40", diff --git a/apps/menu-bar/src/windows/Onboarding.tsx b/apps/menu-bar/src/windows/Onboarding.tsx index 535ec518..88d4b390 100644 --- a/apps/menu-bar/src/windows/Onboarding.tsx +++ b/apps/menu-bar/src/windows/Onboarding.tsx @@ -1,6 +1,7 @@ import { CliCommands } from 'common-types'; import { useCallback, useRef, useState } from 'react'; import { Image, Platform, ScrollView, StyleSheet } from 'react-native'; +import { useWindowFocusEffect } from 'react-native-multi-window'; import { WindowsNavigator } from './index'; import AndroidStudio from '../assets/images/android-studio.png'; @@ -13,7 +14,6 @@ import Button from '../components/Button'; import CommandCheckItem from '../components/CommandCheckItem'; import MenuBarModule from '../modules/MenuBarModule'; import { storage } from '../modules/Storage'; -import { useWindowFocusEffect } from '../modules/WindowManager/useWindowFocus'; import { useExpoTheme } from '../utils/useExpoTheme'; export const hasSeenOnboardingStorageKey = 'has-seen-onboarding'; diff --git a/apps/menu-bar/src/windows/index.ts b/apps/menu-bar/src/windows/index.ts index ab6dbb6a..e67e4ff9 100644 --- a/apps/menu-bar/src/windows/index.ts +++ b/apps/menu-bar/src/windows/index.ts @@ -1,41 +1,54 @@ +import { + WindowStyleMask, + createWindowsNavigator, + withWindowProvider, +} from 'react-native-multi-window'; + import DebugMenu from './DebugMenu'; import Onboarding from './Onboarding'; import Settings from './Settings'; -import { WindowStyleMask, createWindowsNavigator } from '../modules/WindowManager'; +import { withFluentProvider } from '../providers/FluentProvider'; +import { withThemeProvider } from '../utils/useExpoTheme'; + +const wrapComponent = (Component: React.ComponentType, id: string) => + withWindowProvider(withFluentProvider(withThemeProvider(Component)), id); -export const WindowsNavigator = createWindowsNavigator({ - Settings: { - component: Settings, - options: { - title: 'Settings', - windowStyle: { - mask: [WindowStyleMask.Titled, WindowStyleMask.Closable], - titlebarAppearsTransparent: true, - height: 680, - width: 500, +export const WindowsNavigator = createWindowsNavigator( + { + Settings: { + component: Settings, + options: { + title: 'Settings', + windowStyle: { + mask: [WindowStyleMask.Titled, WindowStyleMask.Closable], + titlebarAppearsTransparent: true, + height: 680, + width: 500, + }, }, }, - }, - Onboarding: { - component: Onboarding, - options: { - title: '', - windowStyle: { - mask: [WindowStyleMask.Titled, WindowStyleMask.FullSizeContentView], - titlebarAppearsTransparent: true, - height: 618, - width: 400, + Onboarding: { + component: Onboarding, + options: { + title: '', + windowStyle: { + mask: [WindowStyleMask.Titled, WindowStyleMask.FullSizeContentView], + titlebarAppearsTransparent: true, + height: 618, + width: 400, + }, }, }, - }, - DebugMenu: { - component: DebugMenu, - options: { - title: 'Debug Menu', - windowStyle: { - height: 600, - width: 800, + DebugMenu: { + component: DebugMenu, + options: { + title: 'Debug Menu', + windowStyle: { + height: 600, + width: 800, + }, }, }, }, -}); + wrapComponent +); diff --git a/packages/react-native-multi-window/.eslintrc.js b/packages/react-native-multi-window/.eslintrc.js new file mode 100644 index 00000000..559a33db --- /dev/null +++ b/packages/react-native-multi-window/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + root: true, + extends: 'universe/node', + ignorePatterns: ['build/**', 'node_modules/**'], +}; diff --git a/packages/react-native-multi-window/macos/RNMultiWindow.h b/packages/react-native-multi-window/macos/RNMultiWindow.h new file mode 100644 index 00000000..7a08a156 --- /dev/null +++ b/packages/react-native-multi-window/macos/RNMultiWindow.h @@ -0,0 +1,5 @@ +#import + +@interface RNMultiWindow : NSObject + +@end diff --git a/packages/react-native-multi-window/macos/RNMultiWindow.m b/packages/react-native-multi-window/macos/RNMultiWindow.m new file mode 100644 index 00000000..2c79ab20 --- /dev/null +++ b/packages/react-native-multi-window/macos/RNMultiWindow.m @@ -0,0 +1,46 @@ +#import +#import + +#import "RNMultiWindow.h" +#import "RNMultiWindowNavigator.h" + +@implementation RNMultiWindow + +RCT_EXPORT_MODULE(); + ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + +- (NSDictionary *)constantsToExport +{ + return @{ + @"STYLE_MASK_BORDERLESS": @(NSWindowStyleMaskBorderless), + @"STYLE_MASK_TITLED": @(NSWindowStyleMaskTitled), + @"STYLE_MASK_CLOSABLE": @(NSWindowStyleMaskClosable), + @"STYLE_MASK_MINIATURIZABLE": @(NSWindowStyleMaskMiniaturizable), + @"STYLE_MASK_RESIZABLE": @(NSWindowStyleMaskResizable), + @"STYLE_MASK_UNIFIED_TITLE_AND_TOOLBAR": @(NSWindowStyleMaskUnifiedTitleAndToolbar), + @"STYLE_MASK_FULL_SCREEN": @(NSWindowStyleMaskFullScreen), + @"STYLE_MASK_FULL_SIZE_CONTENT_VIEW": @(NSWindowStyleMaskFullSizeContentView), + @"STYLE_MASK_UTILITY_WINDOW": @(NSWindowStyleMaskUtilityWindow), + @"STYLE_MASK_DOC_MODAL_WINDOW": @(NSWindowStyleMaskDocModalWindow), + @"STYLE_MASK_NONACTIVATING_PANEL": @(NSWindowStyleMaskNonactivatingPanel) + }; +} + +RCT_EXPORT_METHOD(openWindow:(NSString *)moduleName + options:(NSDictionary *)options) +{ + RNMultiWindowNavigator *windowNavigator = [RNMultiWindowNavigator shared]; + [windowNavigator openWindow:moduleName options:options]; +} + +RCT_EXPORT_METHOD(closeWindow:(NSString *)moduleName) +{ + RNMultiWindowNavigator *windowNavigator = [RNMultiWindowNavigator shared]; + [windowNavigator closeWindow:moduleName]; +} + +@end diff --git a/packages/react-native-multi-window/macos/RNMultiWindowNavigator.h b/packages/react-native-multi-window/macos/RNMultiWindowNavigator.h new file mode 100644 index 00000000..b1a0cc05 --- /dev/null +++ b/packages/react-native-multi-window/macos/RNMultiWindowNavigator.h @@ -0,0 +1,18 @@ +#import +#import + +#import "RCTRootViewFactory.h" + +@interface RNMultiWindowNavigator : NSObject + +@property (nonatomic, strong) NSMutableDictionary *windowsMap; + ++ (instancetype)shared; + ++ (void)setRootViewFactory:(RCTRootViewFactory *)factory; ++ (void)setBridge:(RCTBridge *)bridge; + +- (void)openWindow:(NSString *)moduleName options:(NSDictionary *)options; +- (void)closeWindow:(NSString *)moduleName; + +@end diff --git a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.m b/packages/react-native-multi-window/macos/RNMultiWindowNavigator.m similarity index 75% rename from apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.m rename to packages/react-native-multi-window/macos/RNMultiWindowNavigator.m index c5717c97..a8137c64 100644 --- a/apps/menu-bar/macos/ExpoMenuBar-macOS/WindowNavigator.m +++ b/packages/react-native-multi-window/macos/RNMultiWindowNavigator.m @@ -2,13 +2,16 @@ #import #import "WindowWithDeallocCallback.h" -#import "WindowNavigator.h" -#import "Expo_Orbit-Swift.h" +#import "RNMultiWindowNavigator.h" -@implementation WindowNavigator +static RCTRootViewFactory *_rootViewFactory = nil; +// TODO: use RCTBridge current +static RCTBridge *_bridge = nil; + +@implementation RNMultiWindowNavigator + (instancetype)shared { - static WindowNavigator *sharedInstance = nil; + static RNMultiWindowNavigator *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -18,6 +21,14 @@ + (instancetype)shared { return sharedInstance; } ++ (void)setRootViewFactory:(RCTRootViewFactory *)factory { + _rootViewFactory = factory; +} + ++ (void)setBridge:(RCTBridge *)bridge { + _bridge = bridge; +} + - (instancetype)init { self = [super init]; if (self) { @@ -28,7 +39,7 @@ - (instancetype)init { - (void)openWindow:(NSString *)moduleName options:(NSDictionary *)options { dispatch_async(dispatch_get_main_queue(), ^{ - if(self->_windowsMap.count == 0){ + if (self->_windowsMap.count == 0) { [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; } @@ -54,21 +65,22 @@ - (void)openWindow:(NSString *)moduleName options:(NSDictionary *)options { newWindow.deallocCallback = ^{ [self->_windowsMap removeObjectForKey:moduleName]; - if(self->_windowsMap.count == 0){ + if (self->_windowsMap.count == 0) { [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; } }; - RCTReactNativeFactory *reactNativeFactory = [((AppDelegate *)[NSApp delegate])reactNativeFactory]; - RCTPlatformView *rootView = [reactNativeFactory.rootViewFactory viewWithModuleName:moduleName - initialProperties:@{}]; - newWindow.contentView = rootView; + if (_rootViewFactory != nil) { + RCTPlatformView *rootView = [_rootViewFactory viewWithModuleName:moduleName + initialProperties:@{}]; + newWindow.contentView = rootView; + } [self->_windowsMap setObject:newWindow forKey:moduleName]; newWindow.delegate = self; window = newWindow; - }else{ + } else { NSRect contentRect = [window contentRectForFrameRect:[window frame]]; - if(contentRect.size.width != width || contentRect.size.height != height){ + if (contentRect.size.width != width || contentRect.size.height != height) { CGFloat titleBarHeight = [window frame].size.height - contentRect.size.height; NSRect newFrame = NSMakeRect(originX, originY, width, height + titleBarHeight); [window setFrame:newFrame display:YES]; @@ -77,7 +89,7 @@ - (void)openWindow:(NSString *)moduleName options:(NSDictionary *)options { window.title = title; [window setTitlebarAppearsTransparent:titlebarAppearsTransparent]; - if(window.styleMask != windowStyleMask){ + if (window.styleMask != windowStyleMask) { [window setStyleMask:windowStyleMask]; } [window display]; @@ -90,7 +102,7 @@ - (void)openWindow:(NSString *)moduleName options:(NSDictionary *)options { }); } -- (void) closeWindow:(NSString *)moduleName { +- (void)closeWindow:(NSString *)moduleName { dispatch_async(dispatch_get_main_queue(), ^{ NSWindow *window = [self->_windowsMap objectForKey:moduleName]; [window close]; @@ -98,15 +110,18 @@ - (void) closeWindow:(NSString *)moduleName { } - (void)windowDidBecomeKey:(NSNotification *)notification { - RCTBridge *bridge = [((AppDelegate *)[NSApp delegate])bridge]; + if (_bridge == nil) { + return; + } + NSWindow *keyWindow = notification.object; for (NSString *moduleName in self->_windowsMap) { NSWindow *window = self->_windowsMap[moduleName]; if (window == keyWindow) { - [bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit" + [_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit" args:@[@"windowFocused", moduleName]]; - break; + break; } } } diff --git a/packages/react-native-multi-window/macos/WindowWithDeallocCallback.h b/packages/react-native-multi-window/macos/WindowWithDeallocCallback.h new file mode 100644 index 00000000..63fa3dec --- /dev/null +++ b/packages/react-native-multi-window/macos/WindowWithDeallocCallback.h @@ -0,0 +1,9 @@ +#import + +typedef void (^RNMultiWindowDeallocCallback)(void); + +@interface WindowWithDeallocCallback : NSWindow + +@property (nonatomic, copy) RNMultiWindowDeallocCallback deallocCallback; + +@end diff --git a/packages/react-native-multi-window/macos/WindowWithDeallocCallback.m b/packages/react-native-multi-window/macos/WindowWithDeallocCallback.m new file mode 100644 index 00000000..b3ecb809 --- /dev/null +++ b/packages/react-native-multi-window/macos/WindowWithDeallocCallback.m @@ -0,0 +1,11 @@ +#import "WindowWithDeallocCallback.h" + +@implementation WindowWithDeallocCallback + +- (void)dealloc { + if (self.deallocCallback) { + self.deallocCallback(); + } +} + +@end diff --git a/packages/react-native-multi-window/package.json b/packages/react-native-multi-window/package.json new file mode 100644 index 00000000..c2f987c8 --- /dev/null +++ b/packages/react-native-multi-window/package.json @@ -0,0 +1,38 @@ +{ + "name": "react-native-multi-window", + "version": "0.0.2", + "description": "Multi-window management for React Native macOS and Electron", + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "tsc", + "watch": "yarn build --watch --preserveWatchOutput", + "lint": "eslint .", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "react": "*", + "react-native": "^0.81.0", + "react-native-macos": "^0.81.0", + "react-native-electron-modules": "*" + }, + "peerDependenciesMeta": { + "react-native-electron-modules": { + "optional": true + } + }, + "devDependencies": { + "@types/react": "^19.0.0", + "eslint": "^8.57.0", + "eslint-config-universe": "^14.0.0", + "typescript": "^5.8.0" + }, + "homepage": "https://github.com/expo/expo/tree/main/packages/react-native-multi-window", + "repository": { + "type": "git", + "url": "https://github.com/expo/orbit.git", + "directory": "packages/react-native-multi-window" + }, + "author": "Expo", + "license": "MIT" +} diff --git a/packages/react-native-multi-window/react-native-multi-window.podspec b/packages/react-native-multi-window/react-native-multi-window.podspec new file mode 100644 index 00000000..0679d974 --- /dev/null +++ b/packages/react-native-multi-window/react-native-multi-window.podspec @@ -0,0 +1,27 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) + +Pod::Spec.new do |s| + s.name = 'react-native-multi-window' + s.version = package['version'] + s.summary = package['description'] + s.description = package['description'] + s.license = package['license'] + s.author = package['author'] + s.homepage = package['homepage'] + s.platform = :osx, '11.0' + s.source = { git: 'https://github.com/expo/orbit.git' } + s.static_framework = true + + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES' + } + + s.dependency 'React-RCTAppDelegate' + s.dependency 'ReactAppDependencyProvider' + + install_modules_dependencies(s) + + s.source_files = "macos/**/*.{h,m}" +end diff --git a/apps/menu-bar/src/modules/WindowManager/WindowProvider.tsx b/packages/react-native-multi-window/src/WindowProvider.tsx similarity index 100% rename from apps/menu-bar/src/modules/WindowManager/WindowProvider.tsx rename to packages/react-native-multi-window/src/WindowProvider.tsx diff --git a/apps/menu-bar/src/modules/WindowManager/index.ts b/packages/react-native-multi-window/src/index.ts similarity index 51% rename from apps/menu-bar/src/modules/WindowManager/index.ts rename to packages/react-native-multi-window/src/index.ts index 01233544..51081ed9 100644 --- a/apps/menu-bar/src/modules/WindowManager/index.ts +++ b/packages/react-native-multi-window/src/index.ts @@ -1,10 +1,11 @@ import { AppRegistry, NativeModules } from 'react-native'; -import { withWindowProvider } from './WindowProvider'; import { WindowOptions, WindowStyleMask, WindowsConfig, WindowsManagerType } from './types'; -import { withThemeProvider } from '../../utils/useExpoTheme'; export { WindowStyleMask } from './types'; +export type { WindowOptions, WindowsConfig, WindowsManagerType } from './types'; +export { WindowProvider, withWindowProvider, useWindowId } from './WindowProvider'; +export { useWindowFocusEffect } from './useWindowFocus'; type NativeWindowOptions = Omit & { windowStyle?: Omit & { @@ -15,40 +16,41 @@ type NativeWindowOptions = Omit & { type NativeWindowsManagerType = WindowsManagerType & { openWindow: (window: string, options: NativeWindowOptions) => Promise; }; -const WindowsManager: NativeWindowsManagerType = NativeModules.WindowsManager; -const WindowsManagerConstants = NativeModules.WindowsManager.getConstants(); + +const RNMultiWindow: NativeWindowsManagerType = NativeModules.RNMultiWindow; +const RNMultiWindowConstants = NativeModules.RNMultiWindow?.getConstants?.() ?? {}; function getWindowStyleMaskValue(mask: WindowStyleMask) { switch (mask) { case WindowStyleMask.Borderless: - return WindowsManagerConstants.STYLE_MASK_BORDERLESS; + return RNMultiWindowConstants.STYLE_MASK_BORDERLESS; case WindowStyleMask.Titled: - return WindowsManagerConstants.STYLE_MASK_TITLED; + return RNMultiWindowConstants.STYLE_MASK_TITLED; case WindowStyleMask.Closable: - return WindowsManagerConstants.STYLE_MASK_CLOSABLE; + return RNMultiWindowConstants.STYLE_MASK_CLOSABLE; case WindowStyleMask.Miniaturizable: - return WindowsManagerConstants.STYLE_MASK_MINIATURIZABLE; + return RNMultiWindowConstants.STYLE_MASK_MINIATURIZABLE; case WindowStyleMask.Resizable: - return WindowsManagerConstants.STYLE_MASK_RESIZABLE; + return RNMultiWindowConstants.STYLE_MASK_RESIZABLE; case WindowStyleMask.UnifiedTitleAndToolbar: - return WindowsManagerConstants.STYLE_MASK_UNIFIED_TITLE_AND_TOOLBAR; + return RNMultiWindowConstants.STYLE_MASK_UNIFIED_TITLE_AND_TOOLBAR; case WindowStyleMask.FullScreen: - return WindowsManagerConstants.STYLE_MASK_FULL_SCREEN; + return RNMultiWindowConstants.STYLE_MASK_FULL_SCREEN; case WindowStyleMask.FullSizeContentView: - return WindowsManagerConstants.STYLE_MASK_FULL_SIZE_CONTENT_VIEW; + return RNMultiWindowConstants.STYLE_MASK_FULL_SIZE_CONTENT_VIEW; case WindowStyleMask.UtilityWindow: - return WindowsManagerConstants.STYLE_MASK_UTILITY_WINDOW; + return RNMultiWindowConstants.STYLE_MASK_UTILITY_WINDOW; case WindowStyleMask.DocModalWindow: - return WindowsManagerConstants.STYLE_MASK_DOC_MODAL_WINDOW; + return RNMultiWindowConstants.STYLE_MASK_DOC_MODAL_WINDOW; case WindowStyleMask.NonactivatingPanel: - return WindowsManagerConstants.STYLE_MASK_NONACTIVATING_PANEL; + return RNMultiWindowConstants.STYLE_MASK_NONACTIVATING_PANEL; default: - return WindowsManagerConstants.STYLE_MASK_BORDERLESS; + return RNMultiWindowConstants.STYLE_MASK_BORDERLESS; } } function convertMaskArrayToBitwiseOR(mask: WindowStyleMask[]) { - return mask.reduce((result, mask) => result | getWindowStyleMaskValue(mask), 0); + return mask.reduce((result, m) => result | getWindowStyleMaskValue(m), 0); } function convertOptionsToNative(options?: WindowOptions): NativeWindowOptions { @@ -65,19 +67,20 @@ function convertOptionsToNative(options?: WindowOptions): NativeWindowOptions { }; } -export function createWindowsNavigator(config: T) { +export function createWindowsNavigator( + config: T, + wrapComponent: (component: React.ComponentType, id: string) => React.ComponentType +) { Object.entries(config).forEach(([key, value]) => { - AppRegistry.registerComponent(key, () => - withWindowProvider(withThemeProvider(value.component), key) - ); + AppRegistry.registerComponent(key, () => wrapComponent(value.component, key)); }); return { open: (window: keyof T) => { - WindowsManager?.openWindow(String(window), convertOptionsToNative(config[window].options)); + RNMultiWindow?.openWindow(String(window), convertOptionsToNative(config[window].options)); }, close: (window: keyof T) => { - WindowsManager?.closeWindow(String(window)); + RNMultiWindow?.closeWindow(String(window)); }, }; } diff --git a/apps/menu-bar/src/modules/WindowManager/index.web.ts b/packages/react-native-multi-window/src/index.web.ts similarity index 56% rename from apps/menu-bar/src/modules/WindowManager/index.web.ts rename to packages/react-native-multi-window/src/index.web.ts index 4023e658..7bf8029f 100644 --- a/apps/menu-bar/src/modules/WindowManager/index.web.ts +++ b/packages/react-native-multi-window/src/index.web.ts @@ -1,20 +1,21 @@ import { AppRegistry } from 'react-native'; import { requireElectronModule } from 'react-native-electron-modules/build/requireElectronModule'; -import { withWindowProvider } from './WindowProvider'; -import { WindowsConfig, WindowsManagerType } from './types'; -import { withFluentProvider } from '../../providers/FluentProvider'; -import { withThemeProvider } from '../../utils/useExpoTheme'; +import type { WindowsConfig, WindowsManagerType } from './types'; export { WindowStyleMask } from './types'; +export type { WindowOptions, WindowsConfig, WindowsManagerType } from './types'; +export { WindowProvider, withWindowProvider, useWindowId } from './WindowProvider'; +export { useWindowFocusEffect } from './useWindowFocus'; export const WindowManager = requireElectronModule('WindowManager'); -export function createWindowsNavigator(config: T) { +export function createWindowsNavigator( + config: T, + wrapComponent: (component: React.ComponentType, id: string) => React.ComponentType +) { Object.entries(config).forEach(([key, value]) => { - AppRegistry.registerComponent(key, () => - withWindowProvider(withFluentProvider(withThemeProvider(value.component)), key) - ); + AppRegistry.registerComponent(key, () => wrapComponent(value.component, key)); }); return { diff --git a/apps/menu-bar/src/modules/WindowManager/types.ts b/packages/react-native-multi-window/src/types.ts similarity index 100% rename from apps/menu-bar/src/modules/WindowManager/types.ts rename to packages/react-native-multi-window/src/types.ts diff --git a/apps/menu-bar/src/modules/WindowManager/useWindowFocus.ts b/packages/react-native-multi-window/src/useWindowFocus.ts similarity index 88% rename from apps/menu-bar/src/modules/WindowManager/useWindowFocus.ts rename to packages/react-native-multi-window/src/useWindowFocus.ts index dbf643ff..dc0368f7 100644 --- a/apps/menu-bar/src/modules/WindowManager/useWindowFocus.ts +++ b/packages/react-native-multi-window/src/useWindowFocus.ts @@ -1,7 +1,7 @@ import { useEffect } from 'react'; +import { DeviceEventEmitter } from 'react-native'; import { useWindowId } from './WindowProvider'; -import { DeviceEventEmitter } from '../DeviceEventEmitter'; export function useWindowFocusEffect(callback: () => void) { const windowId = useWindowId(); diff --git a/packages/react-native-multi-window/tsconfig.json b/packages/react-native-multi-window/tsconfig.json new file mode 100644 index 00000000..c1439863 --- /dev/null +++ b/packages/react-native-multi-window/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "outDir": "build", + "rootDir": "src", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-native", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock index 4a6b33aa..25dcb626 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3804,6 +3804,13 @@ dependencies: csstype "^3.0.2" +"@types/react@^19.0.0": + version "19.2.10" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.10.tgz#f3ea799e6b4cebad6dfd231c238fc9de7652e2d2" + integrity sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw== + dependencies: + csstype "^3.2.2" + "@types/react@~19.1.10": version "19.1.17" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.17.tgz#8be0b9c546cede389b930a98eb3fad1897f209c3" @@ -5598,6 +5605,11 @@ csstype@^3.0.2, csstype@^3.1.3: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -6291,6 +6303,22 @@ eslint-config-prettier@^9.1.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== +eslint-config-universe@^14.0.0: + version "14.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-universe/-/eslint-config-universe-14.3.0.tgz#d6c562db5e1b02961c139fc57d1cb62ca0dc4caf" + integrity sha512-AQjW1QxwrJPq8CZjTAK7F5q5FeITXcCMwzzoB6pMiDBCEyuUjPE1RbpQNaF/7kpVnxgzgks6rxNwnNIpd50ALg== + dependencies: + "@typescript-eslint/eslint-plugin" "^8.29.1" + "@typescript-eslint/parser" "^8.29.1" + eslint-config-prettier "^9.1.0" + eslint-plugin-import "^2.31.0" + eslint-plugin-n "^17.17.0" + eslint-plugin-node "^11.1.0" + eslint-plugin-prettier "^5.2.6" + eslint-plugin-react "^7.37.5" + eslint-plugin-react-hooks "^5.2.0" + globals "^16.0.0" + eslint-config-universe@^15.0.3: version "15.0.3" resolved "https://registry.yarnpkg.com/eslint-config-universe/-/eslint-config-universe-15.0.3.tgz#4a2c6099a028f86f863542c8e60ac36f504bbb11" @@ -6470,7 +6498,7 @@ eslint-visitor-keys@^4.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== -eslint@^8.57.1: +eslint@^8.57.0, eslint@^8.57.1: version "8.57.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== @@ -13251,7 +13279,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== -typescript@~5.9.2: +typescript@^5.8.0, typescript@~5.9.2: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==