From fb67b503cc6a38d1650500747fc53ff346fd5339 Mon Sep 17 00:00:00 2001 From: Jta26 Date: Tue, 12 Aug 2025 16:42:05 -0700 Subject: [PATCH 1/4] support dialog html element --- packages/react-strict-dom/src/dom/html.js | 8 + packages/react-strict-dom/src/dom/runtime.js | 2 + packages/react-strict-dom/src/native/html.js | 8 + .../types/flowtests/html-types-match.js.flow | 197 +++++++++--------- .../tests/__snapshots__/html-test.js.snap-dom | 138 ++++++++++++ .../__snapshots__/html-test.js.snap-native | 118 +++++++++++ packages/react-strict-dom/tests/html-test.js | 1 + 7 files changed, 376 insertions(+), 96 deletions(-) diff --git a/packages/react-strict-dom/src/dom/html.js b/packages/react-strict-dom/src/dom/html.js index dc60eb9e..69e7a637 100644 --- a/packages/react-strict-dom/src/dom/html.js +++ b/packages/react-strict-dom/src/dom/html.js @@ -110,6 +110,14 @@ export const del: component( ...StrictReactDOMProps ) = createStrict('del', defaultStyles.del); +/** + * "dialog" (block) + */ +export const dialog: component( + ref?: React.RefSetter, + ...StrictReactDOMProps +) = createStrict('dialog', defaultStyles.dialog); + /** * "div" (block) */ diff --git a/packages/react-strict-dom/src/dom/runtime.js b/packages/react-strict-dom/src/dom/runtime.js index 7a455371..1a31bdb7 100644 --- a/packages/react-strict-dom/src/dom/runtime.js +++ b/packages/react-strict-dom/src/dom/runtime.js @@ -90,6 +90,7 @@ const br: StrictReactDOMPropsStyle = null; const button: StrictReactDOMPropsStyle = [styles.inlineblock, styles.button]; const code: StrictReactDOMPropsStyle = [styles.inline, styles.codePre]; const del: StrictReactDOMPropsStyle = null; +const dialog: StrictReactDOMPropsStyle = styles.block; const div: StrictReactDOMPropsStyle = styles.block; const em: StrictReactDOMPropsStyle = styles.inline; const fieldset: StrictReactDOMPropsStyle = styles.block; @@ -139,6 +140,7 @@ export const defaultStyles = { button: button as typeof button, code: code as typeof code, del: del as typeof del, + dialog: dialog as typeof dialog, div: div as typeof div, em: em as typeof em, fieldset: fieldset as typeof fieldset, diff --git a/packages/react-strict-dom/src/native/html.js b/packages/react-strict-dom/src/native/html.js index 7331566f..613ec5c7 100644 --- a/packages/react-strict-dom/src/native/html.js +++ b/packages/react-strict-dom/src/native/html.js @@ -176,6 +176,14 @@ export const del: component( ...StrictReactDOMProps ) = createStrictText('del', { style: styles.lineThrough }); +/** + * "dialog" (block) + */ +export const dialog: component( + ref?: React.RefSetter, + ...StrictReactDOMProps +) = createStrict('dialog'); + /** * "div" (block) */ diff --git a/packages/react-strict-dom/src/types/flowtests/html-types-match.js.flow b/packages/react-strict-dom/src/types/flowtests/html-types-match.js.flow index 5f389a67..54a6464c 100644 --- a/packages/react-strict-dom/src/types/flowtests/html-types-match.js.flow +++ b/packages/react-strict-dom/src/types/flowtests/html-types-match.js.flow @@ -18,241 +18,246 @@ nativeHTML as DomHTML; // Adding all the individual keys as an extra layer of testing declare var a_dom: DomHTML['a']; -(a_dom as NativeHTML['a']); +a_dom as NativeHTML['a']; declare var a_native: NativeHTML['a']; -(a_native as DomHTML['a']); +a_native as DomHTML['a']; declare var article_dom: DomHTML['article']; -(article_dom as NativeHTML['article']); +article_dom as NativeHTML['article']; declare var article_native: NativeHTML['article']; -(article_native as DomHTML['article']); +article_native as DomHTML['article']; declare var aside_dom: DomHTML['aside']; -(aside_dom as NativeHTML['aside']); +aside_dom as NativeHTML['aside']; declare var aside_native: NativeHTML['aside']; -(aside_native as DomHTML['aside']); +aside_native as DomHTML['aside']; declare var b_dom: DomHTML['b']; -(b_dom as NativeHTML['b']); +b_dom as NativeHTML['b']; declare var b_native: NativeHTML['b']; -(b_native as DomHTML['b']); +b_native as DomHTML['b']; declare var bdi_dom: DomHTML['bdi']; -(bdi_dom as NativeHTML['bdi']); +bdi_dom as NativeHTML['bdi']; declare var bdi_native: NativeHTML['bdi']; -(bdi_native as DomHTML['bdi']); +bdi_native as DomHTML['bdi']; declare var bdo_dom: DomHTML['bdo']; -(bdo_dom as NativeHTML['bdo']); +bdo_dom as NativeHTML['bdo']; declare var bdo_native: NativeHTML['bdo']; -(bdo_native as DomHTML['bdo']); +bdo_native as DomHTML['bdo']; declare var blockquote_dom: DomHTML['blockquote']; -(blockquote_dom as NativeHTML['blockquote']); +blockquote_dom as NativeHTML['blockquote']; declare var blockquote_native: NativeHTML['blockquote']; -(blockquote_native as DomHTML['blockquote']); +blockquote_native as DomHTML['blockquote']; declare var br_dom: DomHTML['br']; -(br_dom as NativeHTML['br']); +br_dom as NativeHTML['br']; declare var br_native: NativeHTML['br']; -(br_native as DomHTML['br']); +br_native as DomHTML['br']; declare var button_dom: DomHTML['button']; -(button_dom as NativeHTML['button']); +button_dom as NativeHTML['button']; declare var button_native: NativeHTML['button']; -(button_native as DomHTML['button']); +button_native as DomHTML['button']; declare var code_dom: DomHTML['code']; -(code_dom as NativeHTML['code']); +code_dom as NativeHTML['code']; declare var code_native: NativeHTML['code']; -(code_native as DomHTML['code']); +code_native as DomHTML['code']; declare var del_dom: DomHTML['del']; -(del_dom as NativeHTML['del']); +del_dom as NativeHTML['del']; declare var del_native: NativeHTML['del']; -(del_native as DomHTML['del']); +del_native as DomHTML['del']; + +declare var dialog_dom: DomHTML['dialog']; +dialog_dom as NativeHTML['dialog']; +declare var dialog_native: NativeHTML['dialog']; +dialog_native as DomHTML['dialog']; declare var div_dom: DomHTML['div']; -(div_dom as NativeHTML['div']); +div_dom as NativeHTML['div']; declare var div_native: NativeHTML['div']; -(div_native as DomHTML['div']); +div_native as DomHTML['div']; declare var em_dom: DomHTML['em']; -(em_dom as NativeHTML['em']); +em_dom as NativeHTML['em']; declare var em_native: NativeHTML['em']; -(em_native as DomHTML['em']); +em_native as DomHTML['em']; declare var fieldset_dom: DomHTML['fieldset']; -(fieldset_dom as NativeHTML['fieldset']); +fieldset_dom as NativeHTML['fieldset']; declare var fieldset_native: NativeHTML['fieldset']; -(fieldset_native as DomHTML['fieldset']); +fieldset_native as DomHTML['fieldset']; declare var footer_dom: DomHTML['footer']; -(footer_dom as NativeHTML['footer']); +footer_dom as NativeHTML['footer']; declare var footer_native: NativeHTML['footer']; -(footer_native as DomHTML['footer']); +footer_native as DomHTML['footer']; declare var form_dom: DomHTML['form']; -(form_dom as NativeHTML['form']); +form_dom as NativeHTML['form']; declare var form_native: NativeHTML['form']; -(form_native as DomHTML['form']); +form_native as DomHTML['form']; declare var h1_dom: DomHTML['h1']; -(h1_dom as NativeHTML['h1']); +h1_dom as NativeHTML['h1']; declare var h1_native: NativeHTML['h1']; -(h1_native as DomHTML['h1']); +h1_native as DomHTML['h1']; declare var h2_dom: DomHTML['h2']; -(h2_dom as NativeHTML['h2']); +h2_dom as NativeHTML['h2']; declare var h2_native: NativeHTML['h2']; -(h2_native as DomHTML['h2']); +h2_native as DomHTML['h2']; declare var h3_dom: DomHTML['h3']; -(h3_dom as NativeHTML['h3']); +h3_dom as NativeHTML['h3']; declare var h3_native: NativeHTML['h3']; -(h3_native as DomHTML['h3']); +h3_native as DomHTML['h3']; declare var h4_dom: DomHTML['h4']; -(h4_dom as NativeHTML['h4']); +h4_dom as NativeHTML['h4']; declare var h4_native: NativeHTML['h4']; -(h4_native as DomHTML['h4']); +h4_native as DomHTML['h4']; declare var h5_dom: DomHTML['h5']; -(h5_dom as NativeHTML['h5']); +h5_dom as NativeHTML['h5']; declare var h5_native: NativeHTML['h5']; -(h5_native as DomHTML['h5']); +h5_native as DomHTML['h5']; declare var h6_dom: DomHTML['h6']; -(h6_dom as NativeHTML['h6']); +h6_dom as NativeHTML['h6']; declare var h6_native: NativeHTML['h6']; -(h6_native as DomHTML['h6']); +h6_native as DomHTML['h6']; declare var header_dom: DomHTML['header']; -(header_dom as NativeHTML['header']); +header_dom as NativeHTML['header']; declare var header_native: NativeHTML['header']; -(header_native as DomHTML['header']); +header_native as DomHTML['header']; declare var hr_dom: DomHTML['hr']; -(hr_dom as NativeHTML['hr']); +hr_dom as NativeHTML['hr']; declare var hr_native: NativeHTML['hr']; -(hr_native as DomHTML['hr']); +hr_native as DomHTML['hr']; declare var i_dom: DomHTML['i']; -(i_dom as NativeHTML['i']); +i_dom as NativeHTML['i']; declare var i_native: NativeHTML['i']; -(i_native as DomHTML['i']); +i_native as DomHTML['i']; declare var img_dom: DomHTML['img']; -(img_dom as NativeHTML['img']); +img_dom as NativeHTML['img']; declare var img_native: NativeHTML['img']; -(img_native as DomHTML['img']); +img_native as DomHTML['img']; declare var input_dom: DomHTML['input']; -(input_dom as NativeHTML['input']); +input_dom as NativeHTML['input']; declare var input_native: NativeHTML['input']; -(input_native as DomHTML['input']); +input_native as DomHTML['input']; declare var ins_dom: DomHTML['ins']; -(ins_dom as NativeHTML['ins']); +ins_dom as NativeHTML['ins']; declare var ins_native: NativeHTML['ins']; -(ins_native as DomHTML['ins']); +ins_native as DomHTML['ins']; declare var kbd_dom: DomHTML['kbd']; -(kbd_dom as NativeHTML['kbd']); +kbd_dom as NativeHTML['kbd']; declare var kbd_native: NativeHTML['kbd']; -(kbd_native as DomHTML['kbd']); +kbd_native as DomHTML['kbd']; declare var label_dom: DomHTML['label']; -(label_dom as NativeHTML['label']); +label_dom as NativeHTML['label']; declare var label_native: NativeHTML['label']; -(label_native as DomHTML['label']); +label_native as DomHTML['label']; declare var li_dom: DomHTML['li']; -(li_dom as NativeHTML['li']); +li_dom as NativeHTML['li']; declare var li_native: NativeHTML['li']; -(li_native as DomHTML['li']); +li_native as DomHTML['li']; declare var main_dom: DomHTML['main']; -(main_dom as NativeHTML['main']); +main_dom as NativeHTML['main']; declare var main_native: NativeHTML['main']; -(main_native as DomHTML['main']); +main_native as DomHTML['main']; declare var nav_dom: DomHTML['nav']; -(nav_dom as NativeHTML['nav']); +nav_dom as NativeHTML['nav']; declare var nav_native: NativeHTML['nav']; -(nav_native as DomHTML['nav']); +nav_native as DomHTML['nav']; declare var ol_dom: DomHTML['ol']; -(ol_dom as NativeHTML['ol']); +ol_dom as NativeHTML['ol']; declare var ol_native: NativeHTML['ol']; -(ol_native as DomHTML['ol']); +ol_native as DomHTML['ol']; declare var p_dom: DomHTML['p']; -(p_dom as NativeHTML['p']); +p_dom as NativeHTML['p']; declare var p_native: NativeHTML['p']; -(p_native as DomHTML['p']); +p_native as DomHTML['p']; declare var pre_dom: DomHTML['pre']; -(pre_dom as NativeHTML['pre']); +pre_dom as NativeHTML['pre']; declare var pre_native: NativeHTML['pre']; -(pre_native as DomHTML['pre']); +pre_native as DomHTML['pre']; declare var option_dom: DomHTML['option']; -(option_dom as NativeHTML['option']); +option_dom as NativeHTML['option']; declare var option_native: NativeHTML['option']; -(option_native as DomHTML['option']); +option_native as DomHTML['option']; declare var optgroup_dom: DomHTML['optgroup']; -(optgroup_dom as NativeHTML['optgroup']); +optgroup_dom as NativeHTML['optgroup']; declare var optgroup_native: NativeHTML['optgroup']; -(optgroup_native as DomHTML['optgroup']); +optgroup_native as DomHTML['optgroup']; declare var s_dom: DomHTML['s']; -(s_dom as NativeHTML['s']); +s_dom as NativeHTML['s']; declare var s_native: NativeHTML['s']; -(s_native as DomHTML['s']); +s_native as DomHTML['s']; declare var section_dom: DomHTML['section']; -(section_dom as NativeHTML['section']); +section_dom as NativeHTML['section']; declare var section_native: NativeHTML['section']; -(section_native as DomHTML['section']); +section_native as DomHTML['section']; declare var select_dom: DomHTML['select']; -(select_dom as NativeHTML['select']); +select_dom as NativeHTML['select']; declare var select_native: NativeHTML['select']; -(select_native as DomHTML['select']); +select_native as DomHTML['select']; declare var span_dom: DomHTML['span']; -(span_dom as NativeHTML['span']); +span_dom as NativeHTML['span']; declare var span_native: NativeHTML['span']; -(span_native as DomHTML['span']); +span_native as DomHTML['span']; declare var strong_dom: DomHTML['strong']; -(strong_dom as NativeHTML['strong']); +strong_dom as NativeHTML['strong']; declare var strong_native: NativeHTML['strong']; -(strong_native as DomHTML['strong']); +strong_native as DomHTML['strong']; declare var sub_dom: DomHTML['sub']; -(sub_dom as NativeHTML['sub']); +sub_dom as NativeHTML['sub']; declare var sub_native: NativeHTML['sub']; -(sub_native as DomHTML['sub']); +sub_native as DomHTML['sub']; declare var sup_dom: DomHTML['sup']; -(sup_dom as NativeHTML['sup']); +sup_dom as NativeHTML['sup']; declare var sup_native: NativeHTML['sup']; -(sup_native as DomHTML['sup']); +sup_native as DomHTML['sup']; declare var textarea_dom: DomHTML['textarea']; -(textarea_dom as NativeHTML['textarea']); +textarea_dom as NativeHTML['textarea']; declare var textarea_native: NativeHTML['textarea']; -(textarea_native as DomHTML['textarea']); +textarea_native as DomHTML['textarea']; declare var u_dom: DomHTML['u']; -(u_dom as NativeHTML['u']); +u_dom as NativeHTML['u']; declare var u_native: NativeHTML['u']; -(u_native as DomHTML['u']); +u_native as DomHTML['u']; declare var ul_dom: DomHTML['ul']; -(ul_dom as NativeHTML['ul']); +ul_dom as NativeHTML['ul']; declare var ul_native: NativeHTML['ul']; -(ul_native as DomHTML['ul']); +ul_native as DomHTML['ul']; diff --git a/packages/react-strict-dom/tests/__snapshots__/html-test.js.snap-dom b/packages/react-strict-dom/tests/__snapshots__/html-test.js.snap-dom index deddcde5..4e175e74 100644 --- a/packages/react-strict-dom/tests/__snapshots__/html-test.js.snap-dom +++ b/packages/react-strict-dom/tests/__snapshots__/html-test.js.snap-dom @@ -1543,6 +1543,144 @@ exports[`html "del" supports inline event handlers 1`] = ` /> `; +exports[`html "dialog" default rendering 1`] = ` + +`; + +exports[`html "dialog" ignores and warns about unsupported attributes 1`] = ` + +`; + +exports[`html "dialog" supports global attributes 1`] = ` + +`; + +exports[`html "dialog" supports inline event handlers 1`] = ` + +`; + exports[`html "div" default rendering 1`] = `
`; +exports[`html "dialog" default rendering 1`] = ` + +`; + +exports[`html "dialog" ignores and warns about unsupported attributes 1`] = ` + +`; + +exports[`html "dialog" supports global attributes 1`] = ` + + + children + + +`; + +exports[`html "dialog" supports inline event handlers 1`] = ` + +`; + exports[`html "div" default rendering 1`] = ` Date: Thu, 14 Aug 2025 11:29:03 -0700 Subject: [PATCH 2/4] map ::backdrop backgroundColor to RN modal backdropColor --- .../react-strict-dom/src/native/css/index.js | 2 ++ .../src/native/css/processStyle.js | 16 +++++++++++++++- .../src/types/renderer.native.js | 5 +++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/react-strict-dom/src/native/css/index.js b/packages/react-strict-dom/src/native/css/index.js index 67c47d57..7f687f4f 100644 --- a/packages/react-strict-dom/src/native/css/index.js +++ b/packages/react-strict-dom/src/native/css/index.js @@ -452,6 +452,8 @@ export function props( // '::placeholder' polyfill else if (styleProp === 'placeholderTextColor') { nativeProps.placeholderTextColor = styleValue; + } else if (styleProp === 'backdropColor') { + nativeProps.backdropColor = styleValue; } // visibility polyfill // Note: we can't polyfill nested visibility changes diff --git a/packages/react-strict-dom/src/native/css/processStyle.js b/packages/react-strict-dom/src/native/css/processStyle.js index 70078a1c..2b7a91e8 100644 --- a/packages/react-strict-dom/src/native/css/processStyle.js +++ b/packages/react-strict-dom/src/native/css/processStyle.js @@ -79,7 +79,6 @@ export function processStyle( } continue; } - // Object values else if (typeof styleValue === 'object' && styleValue != null) { if (propName === '::placeholder') { @@ -97,6 +96,21 @@ export function processStyle( } } continue; + } else if (propName === '::backdrop') { + const backdropStyleProps = Object.keys(styleValue); + for (let i = 0; i < backdropStyleProps.length; i++) { + const prop = backdropStyleProps[i]; + if (prop === 'backgroundColor') { + result['backdropColor'] = processStyle({ + backgroundColor: styleValue.backgroundColor + }).backgroundColor; + } else { + if (__DEV__) { + warnMsg(`unsupported "::backdrop" style property "${prop}"`); + } + } + } + continue; } else if (Object.hasOwn(styleValue, 'default')) { result[propName] = processStyle(styleValue); continue; diff --git a/packages/react-strict-dom/src/types/renderer.native.js b/packages/react-strict-dom/src/types/renderer.native.js index a8c59d4e..eac6f3a0 100644 --- a/packages/react-strict-dom/src/types/renderer.native.js +++ b/packages/react-strict-dom/src/types/renderer.native.js @@ -31,6 +31,10 @@ import type { // $FlowFixMe(nonstrict-import) ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; +import type { + // $FlowFixMe(nonstrict-import) + ModalProps +} from 'react-native/Libraries/Modal/Modal'; type ReactNativeProps = { accessible?: ViewProps['accessible'], @@ -105,6 +109,7 @@ type ReactNativeProps = { onTouchMove?: ViewProps['onTouchMove'], placeholder?: TextInputProps['placeholder'], placeholderTextColor?: TextInputProps['placeholderTextColor'], + backdropColor?: ModalProps['backdropColor'], pointerEvents?: ViewProps['pointerEvents'], ref?: $FlowFixMe, referrerPolicy?: ImageProps['referrerPolicy'], From 04b6fc38f0d53628e0f6093e200c009fe78b25c8 Mon Sep 17 00:00:00 2001 From: Jta26 Date: Thu, 14 Aug 2025 14:25:09 -0700 Subject: [PATCH 3/4] render Modal in native --- .../native/modules/createStrictDOMComponent.js | 15 +++++++++++---- .../src/native/react-native/index.js | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js b/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js index 26157686..dceb087a 100644 --- a/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js +++ b/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js @@ -37,10 +37,17 @@ export function createStrictDOMComponent( ): component(ref?: React.RefSetter, ...P) { const component: React.AbstractComponent = React.forwardRef( function (props, forwardedRef) { - let NativeComponent = - tagName === 'button' - ? ReactNative.Pressable - : ReactNative.ViewNativeComponent; + let NativeComponent = null; + switch (tagName) { + case 'button': + NativeComponent = ReactNative.Pressable; + break; + case 'dialog': + NativeComponent = ReactNative.Modal; + break; + default: + NativeComponent = ReactNative.ViewNativeComponent; + } const elementRef = useStrictDOMElement(forwardedRef, { tagName }); const hasTextAncestor = React.useContext(ReactNative.TextAncestorContext); diff --git a/packages/react-strict-dom/src/native/react-native/index.js b/packages/react-strict-dom/src/native/react-native/index.js index 6c7da342..5ca07205 100644 --- a/packages/react-strict-dom/src/native/react-native/index.js +++ b/packages/react-strict-dom/src/native/react-native/index.js @@ -14,7 +14,8 @@ export { Platform, Pressable, Text, - TextInput + TextInput, + Modal } from 'react-native'; export { LayoutConformance } from './LayoutConformance'; export { TextAncestorContext } from './TextAncestorContext'; From 153c773193474506b9834e7edf3b0df55d67cb5e Mon Sep 17 00:00:00 2001 From: Jta26 Date: Fri, 15 Aug 2025 11:24:59 -0700 Subject: [PATCH 4/4] dialog expo example --- apps/examples/src/components/App.js | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/apps/examples/src/components/App.js b/apps/examples/src/components/App.js index 1801e4a7..cb7ec079 100644 --- a/apps/examples/src/components/App.js +++ b/apps/examples/src/components/App.js @@ -8,6 +8,7 @@ */ import * as React from 'react'; +import { useRef } from 'react'; import { ScrollView } from 'react-native'; import { css, html } from 'react-strict-dom'; import { tokens, themeColors, systemColors } from './tokens.stylex'; @@ -639,11 +640,48 @@ function Shell(): React.MixedElement { /> {imageErrorText} + + + ); } +function DialogExample(): React.MixedElement { + const dialogRef = useRef(null); + // const [isModalVisible, setIsModalVisible] = useState(false); + + const showModal = () => { + if (dialogRef.current) { + // dialogRef.current.showModal(); + // setIsModalVisible(true); + console.log('Opening Modal'); + } + }; + + const closeModal = () => { + if (dialogRef.current) { + // dialogRef.current.close(); + // setIsModalVisible(false); + console.log('Closing Modal'); + } + }; + + return ( + + Open Dialog + + + Dialog Title + This is a dialog content. + Close + + + + ); +} + export default function App(): React.MixedElement { return ( @@ -886,5 +924,20 @@ const styles = css.create({ borderBlockWidth: 20, borderInlineWidth: 20, borderStyle: 'solid' + }, + dialog: { + width: 200, + height: 200, + margin: 0, + backgroundColor: 'red', + '::backdrop': { + backgroundColor: 'rgba(30, 41, 196, 0.5)' + } + }, + dialogcontent: { + width: 200, + height: 200, + margin: 'auto', + position: 'relative' } });