diff --git a/change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json b/change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json new file mode 100644 index 00000000000..2b7577436b5 --- /dev/null +++ b/change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Add hideTitleBar and hideBorder to Modal", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js index bd0dfc7383f..4015de904c5 100644 --- a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js +++ b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js @@ -19,7 +19,7 @@ import {RNTesterThemeContext} from '../../components/RNTesterTheme'; import RNTOption from '../../components/RNTOption'; import * as React from 'react'; import {useCallback, useContext, useState} from 'react'; -import {Modal, Platform, StyleSheet, Switch, Text, View} from 'react-native'; +import {Modal, Platform, StyleSheet, Switch, Text, TextInput, View} from 'react-native'; const animationTypes = ['slide', 'none', 'fade'] as const; const presentationStyles = [ @@ -70,6 +70,9 @@ function ModalPresentation() { ios: ['portrait'], }), transparent: false, + hideBorder: false, // Windows + hideTitleBar: false, // Windows + title: 'Modal Presentation', // Windows visible: false, }); const presentationStyle = props.presentationStyle; @@ -190,6 +193,37 @@ function ModalPresentation() { } /> + {/* [Windows] - HideTitleBar is a Windows only prop. It is not supported on iOS or Android. */} + + HideTitleBar + + setProps(prev => ({...prev, hideTitleBar: enabled})) + } + /> + + {/* [Windows] - HideBorder is a Windows only prop. It is not supported on iOS or Android. */} + + HideBorder + + setProps(prev => ({...prev, hideBorder: enabled})) + } + /> + + {/* [Windows] - Title is a Windows only prop. It is not supported on iOS or Android. */} + + Title + + setProps(prev => ({...prev, title: text})) + } + /> + {Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? ( iOS Modal can only be transparent with 'overFullScreen' Presentation diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap index 336fd341df5..b0695e2cae6 100644 --- a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap +++ b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap @@ -31656,6 +31656,119 @@ exports[`snapshotAllPages Modal 1`] = ` value={false} /> + + + HideTitleBar + + + + + + HideBorder + + + + + + Title + + + +#include #include #include #include @@ -112,6 +113,12 @@ struct ModalHostView : public winrt::implements::UpdateProps(view, newProps, oldProps); } @@ -258,6 +265,61 @@ struct ModalHostView : public winrt::implementshideTitleBar.value_or(false) && m_localProps->hideBorder.value_or(false); + + auto titleBar = m_rnWindow.AppWindow().TitleBar(); + titleBar.ResetToDefault(); + overlappedPresenter.IsResizable(false); + + overlappedPresenter.SetBorderAndTitleBar(!hideBorders, !m_localProps->hideTitleBar.value_or(false)); + + auto hwnd = GetWindowFromWindowId(m_rnWindow.AppWindow().Id()); + + if (hideBorders) { + const DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_DISABLED; + ::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy)); + + const DWM_WINDOW_CORNER_PREFERENCE cornerPref = DWMWCP_DEFAULT; + ::DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref)); + + const COLORREF zeroColor = 0; + ::DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &zeroColor, sizeof(zeroColor)); + ::DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &zeroColor, sizeof(zeroColor)); + ::DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, &zeroColor, sizeof(zeroColor)); + } else { + const DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_USEWINDOWSTYLE; + ::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy)); + + const DWM_WINDOW_CORNER_PREFERENCE cornerPref = DWMWCP_DEFAULT; + ::DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref)); + + const COLORREF zeroColor = DWMWA_COLOR_DEFAULT; + ::DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &zeroColor, sizeof(zeroColor)); + ::DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &zeroColor, sizeof(zeroColor)); + ::DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, &zeroColor, sizeof(zeroColor)); + + titleBar.IconShowOptions(winrt::Microsoft::UI::Windowing::IconShowOptions::HideIconAndSystemMenu); + } + } + // creates a new modal window void EnsureModalCreated(const winrt::Microsoft::ReactNative::ComponentView &view) { if (m_popUp) { @@ -282,22 +344,8 @@ struct ModalHostView : public winrt::implementstitle.has_value()) { diff --git a/vnext/src-win/Libraries/Modal/Modal.d.ts b/vnext/src-win/Libraries/Modal/Modal.d.ts index 66dc9abe8a0..f345348bae9 100644 --- a/vnext/src-win/Libraries/Modal/Modal.d.ts +++ b/vnext/src-win/Libraries/Modal/Modal.d.ts @@ -117,6 +117,10 @@ export interface ModalWindowsProps { /* title for the modal, shown in the title bar */ // [Windows title?: string | undefined; + + hideTitleBar?: boolean | undefined; + + hideBorder?: boolean | undefined; // Windows] } diff --git a/vnext/src-win/Libraries/Modal/Modal.windows.js b/vnext/src-win/Libraries/Modal/Modal.windows.js index 200e0239044..d02116df78b 100644 --- a/vnext/src-win/Libraries/Modal/Modal.windows.js +++ b/vnext/src-win/Libraries/Modal/Modal.windows.js @@ -174,6 +174,8 @@ export type ModalPropsWindows = { * [Windows] The `title` prop sets the title of the modal window. */ title?: ?string, + hideTitleBar?: ?boolean, + hideBorder?: ?boolean, }; export type ModalProps = { @@ -352,6 +354,8 @@ class Modal extends React.Component { onOrientationChange={this.props.onOrientationChange} allowSwipeDismissal={this.props.allowSwipeDismissal} testID={this.props.testID} + hideTitleBar={this.props.hideTitleBar} // [Windows] + hideBorder={this.props.hideBorder} // [Windows] title={this.props.title}> diff --git a/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js b/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js index 4f1e3f8077a..b8fb6976a77 100644 --- a/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js +++ b/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js @@ -152,6 +152,10 @@ type RCTModalHostViewNativeProps = Readonly<{| */ // [Windows title?: WithDefault, + + hideTitleBar?: WithDefault, + + hideBorder?: WithDefault, // Windows] |}>;