mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-07 05:36:42 +00:00
[fix] 'render' export uses ReactDOM.createRoot
AppRegistry.runApplication uses `ReactDOM.createRoot` by default, but the `render` export uses the legacy `ReactDOM.render`. This patch makes those 2 APIs consistent. It also makes some adjustments to the `createSheet` internals to more reliably implement and test style sheet replication within ShadowRoot's and iframes. Fix #2612
This commit is contained in:
@@ -1,51 +1,137 @@
|
||||
import React from 'react';
|
||||
import { AppRegistry, Text, StyleSheet } from 'react-native';
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { Pressable, Text, StyleSheet, View, render } from 'react-native';
|
||||
import Example from '../../shared/example';
|
||||
|
||||
function App() {
|
||||
return <Text style={styles.text}>Should be red and bold</Text>;
|
||||
function IframeWrapper({ children }) {
|
||||
const iframeHost = useRef();
|
||||
const reactRoot = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (iframeHost.current) {
|
||||
if (!reactRoot.current) {
|
||||
const iframeElement = iframeHost.current;
|
||||
const iframeAppContainer = document.createElement('div');
|
||||
iframeElement.contentWindow.document.body.appendChild(
|
||||
iframeAppContainer
|
||||
);
|
||||
reactRoot.current = render(children, iframeAppContainer);
|
||||
}
|
||||
reactRoot.current.render(children);
|
||||
}
|
||||
});
|
||||
|
||||
return <iframe ref={iframeHost} style={{ border: 'none' }} />;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
color: 'red',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
});
|
||||
function ShadowDomWrapper({ children }) {
|
||||
const shadowHost = useRef();
|
||||
const reactRoot = useRef();
|
||||
|
||||
AppRegistry.registerComponent('App', () => App);
|
||||
useEffect(() => {
|
||||
if (shadowHost.current) {
|
||||
if (!reactRoot.current) {
|
||||
const shadowRoot = shadowHost.current.attachShadow({ mode: 'open' });
|
||||
reactRoot.current = render(children, shadowRoot);
|
||||
}
|
||||
reactRoot.current.render(children);
|
||||
}
|
||||
});
|
||||
|
||||
export default function AppStatePage() {
|
||||
const iframeRef = React.useRef(null);
|
||||
const shadowRef = React.useRef(null);
|
||||
return <div ref={shadowHost} />;
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const iframeElement = iframeRef.current;
|
||||
const iframeBody = iframeElement.contentWindow.document.body;
|
||||
const iframeRootTag = document.createElement('div');
|
||||
iframeRootTag.id = 'iframe-root';
|
||||
iframeBody.appendChild(iframeRootTag);
|
||||
const app1 = AppRegistry.runApplication('App', { rootTag: iframeRootTag });
|
||||
function Heading({ children }) {
|
||||
return (
|
||||
<Text role="heading" style={styles.heading}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const shadowElement = shadowRef.current;
|
||||
const shadowRoot = shadowElement.attachShadow({ mode: 'open' });
|
||||
const shadowRootTag = document.createElement('div');
|
||||
shadowRootTag.id = 'shadow-root';
|
||||
shadowRoot.appendChild(shadowRootTag);
|
||||
const app2 = AppRegistry.runApplication('App', { rootTag: shadowRootTag });
|
||||
function Button({ active, onPress, title }) {
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => onPress((old) => !old)}
|
||||
style={[styles.button, active && styles.buttonActive]}
|
||||
>
|
||||
<Text style={styles.buttonText}>{title}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
app1.unmount();
|
||||
app2.unmount();
|
||||
};
|
||||
}, []);
|
||||
function App() {
|
||||
const [active, setActive] = useState(false);
|
||||
const [activeIframe, setActiveIframe] = useState(false);
|
||||
const [activeShadow, setActiveShadow] = useState(false);
|
||||
|
||||
return (
|
||||
<Example title="AppRegistry">
|
||||
<Text>Styles in iframe</Text>
|
||||
<iframe ref={iframeRef} />
|
||||
<Text>Styles in ShadowRoot</Text>
|
||||
<div ref={shadowRef} />
|
||||
<View style={styles.app}>
|
||||
<View style={styles.header}>
|
||||
<Heading>Styles in document</Heading>
|
||||
<Text style={styles.text}>Should be red and bold</Text>
|
||||
<Button active={active} onPress={setActive} title={'Button'} />
|
||||
|
||||
<Heading>Styles in ShadowRoot</Heading>
|
||||
<ShadowDomWrapper>
|
||||
<Text style={styles.text}>Should be red and bold</Text>
|
||||
<Button
|
||||
active={activeShadow}
|
||||
onPress={setActiveShadow}
|
||||
title={'Button'}
|
||||
/>
|
||||
</ShadowDomWrapper>
|
||||
|
||||
<Heading>Styles in iframe</Heading>
|
||||
<IframeWrapper>
|
||||
<Text style={styles.text}>Should be red and bold</Text>
|
||||
<Button
|
||||
active={activeIframe}
|
||||
onPress={setActiveIframe}
|
||||
title={'Button'}
|
||||
/>
|
||||
</IframeWrapper>
|
||||
</View>
|
||||
</View>
|
||||
</Example>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
app: {
|
||||
marginHorizontal: 'auto',
|
||||
maxWidth: 500
|
||||
},
|
||||
header: {
|
||||
padding: 20
|
||||
},
|
||||
heading: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: '1.125rem',
|
||||
marginBlockStart: '1rem',
|
||||
marginBlockEnd: '0.25rem'
|
||||
},
|
||||
text: {
|
||||
color: 'red',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
button: {
|
||||
backgroundColor: 'red',
|
||||
paddingBlock: 5,
|
||||
paddingInline: 10
|
||||
},
|
||||
buttonActive: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
buttonText: {
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
textTransform: 'uppercase',
|
||||
userSelect: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
export default function AppStatePage() {
|
||||
return <App />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AppRegistry runApplication styles roots in iframes: iframe css 1`] = `
|
||||
"[stylesheet-group=\\"0\\"]{}
|
||||
body{margin:0;}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
[stylesheet-group=\\"1\\"]{}
|
||||
.css-view-175oi2r{align-items:stretch;background-color:rgba(0,0,0,0.00);border:0 solid black;box-sizing:border-box;display:flex;flex-basis:auto;flex-direction:column;flex-shrink:0;list-style:none;margin:0px;min-height:0px;min-width:0px;padding:0px;position:relative;text-decoration:none;z-index:0;}
|
||||
[stylesheet-group=\\"2\\"]{}
|
||||
.r-display-xoduu5{display:inline-flex;}
|
||||
.r-flex-13awgt0{flex:1;}
|
||||
[stylesheet-group=\\"3\\"]{}
|
||||
.r-bottom-1p0dtai{bottom:0px;}
|
||||
.r-left-1d2f490{left:0px;}
|
||||
.r-pointerEvents-105ug2t{pointer-events:auto!important;}
|
||||
.r-pointerEvents-12vffkv>*{pointer-events:auto;}
|
||||
.r-pointerEvents-12vffkv{pointer-events:none!important;}
|
||||
.r-pointerEvents-633pao{pointer-events:none!important;}
|
||||
.r-pointerEvents-ah5dr5>*{pointer-events:none;}
|
||||
.r-pointerEvents-ah5dr5{pointer-events:auto!important;}
|
||||
.r-position-u8s1d{position:absolute;}
|
||||
.r-right-zchlnj{right:0px;}
|
||||
.r-top-ipm5af{top:0px;}"
|
||||
`;
|
||||
|
||||
exports[`AppRegistry runApplication styles roots in iframes: iframe css 2`] = `
|
||||
"[stylesheet-group=\\"0\\"]{}
|
||||
body{margin:0;}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
[stylesheet-group=\\"1\\"]{}
|
||||
.css-view-175oi2r{align-items:stretch;background-color:rgba(0,0,0,0.00);border:0 solid black;box-sizing:border-box;display:flex;flex-basis:auto;flex-direction:column;flex-shrink:0;list-style:none;margin:0px;min-height:0px;min-width:0px;padding:0px;position:relative;text-decoration:none;z-index:0;}
|
||||
[stylesheet-group=\\"2\\"]{}
|
||||
.r-display-xoduu5{display:inline-flex;}
|
||||
.r-flex-13awgt0{flex:1;}
|
||||
[stylesheet-group=\\"3\\"]{}
|
||||
.r-bottom-1p0dtai{bottom:0px;}
|
||||
.r-left-1d2f490{left:0px;}
|
||||
.r-pointerEvents-105ug2t{pointer-events:auto!important;}
|
||||
.r-pointerEvents-12vffkv>*{pointer-events:auto;}
|
||||
.r-pointerEvents-12vffkv{pointer-events:none!important;}
|
||||
.r-pointerEvents-633pao{pointer-events:none!important;}
|
||||
.r-pointerEvents-ah5dr5>*{pointer-events:none;}
|
||||
.r-pointerEvents-ah5dr5{pointer-events:auto!important;}
|
||||
.r-position-u8s1d{position:absolute;}
|
||||
.r-right-zchlnj{right:0px;}
|
||||
.r-top-ipm5af{top:0px;}"
|
||||
`;
|
||||
|
||||
exports[`AppRegistry runApplication styles roots in shadow trees: shadow dom css 1`] = `
|
||||
"[stylesheet-group=\\"0\\"]{}
|
||||
body{margin:0;}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
[stylesheet-group=\\"1\\"]{}
|
||||
.css-view-175oi2r{align-items:stretch;background-color:rgba(0,0,0,0.00);border:0 solid black;box-sizing:border-box;display:flex;flex-basis:auto;flex-direction:column;flex-shrink:0;list-style:none;margin:0px;min-height:0px;min-width:0px;padding:0px;position:relative;text-decoration:none;z-index:0;}
|
||||
[stylesheet-group=\\"2\\"]{}
|
||||
.r-display-xoduu5{display:inline-flex;}
|
||||
.r-flex-13awgt0{flex:1;}
|
||||
[stylesheet-group=\\"3\\"]{}
|
||||
.r-bottom-1p0dtai{bottom:0px;}
|
||||
.r-left-1d2f490{left:0px;}
|
||||
.r-pointerEvents-105ug2t{pointer-events:auto!important;}
|
||||
.r-pointerEvents-12vffkv>*{pointer-events:auto;}
|
||||
.r-pointerEvents-12vffkv{pointer-events:none!important;}
|
||||
.r-pointerEvents-633pao{pointer-events:none!important;}
|
||||
.r-pointerEvents-ah5dr5>*{pointer-events:none;}
|
||||
.r-pointerEvents-ah5dr5{pointer-events:auto!important;}
|
||||
.r-position-u8s1d{position:absolute;}
|
||||
.r-right-zchlnj{right:0px;}
|
||||
.r-top-ipm5af{top:0px;}"
|
||||
`;
|
||||
|
||||
exports[`AppRegistry runApplication styles roots in shadow trees: shadow dom css 2`] = `
|
||||
"[stylesheet-group=\\"0\\"]{}
|
||||
body{margin:0;}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
[stylesheet-group=\\"1\\"]{}
|
||||
.css-view-175oi2r{align-items:stretch;background-color:rgba(0,0,0,0.00);border:0 solid black;box-sizing:border-box;display:flex;flex-basis:auto;flex-direction:column;flex-shrink:0;list-style:none;margin:0px;min-height:0px;min-width:0px;padding:0px;position:relative;text-decoration:none;z-index:0;}
|
||||
[stylesheet-group=\\"2\\"]{}
|
||||
.r-display-xoduu5{display:inline-flex;}
|
||||
.r-flex-13awgt0{flex:1;}
|
||||
[stylesheet-group=\\"3\\"]{}
|
||||
.r-bottom-1p0dtai{bottom:0px;}
|
||||
.r-left-1d2f490{left:0px;}
|
||||
.r-pointerEvents-105ug2t{pointer-events:auto!important;}
|
||||
.r-pointerEvents-12vffkv>*{pointer-events:auto;}
|
||||
.r-pointerEvents-12vffkv{pointer-events:none!important;}
|
||||
.r-pointerEvents-633pao{pointer-events:none!important;}
|
||||
.r-pointerEvents-ah5dr5>*{pointer-events:none;}
|
||||
.r-pointerEvents-ah5dr5{pointer-events:auto!important;}
|
||||
.r-position-u8s1d{position:absolute;}
|
||||
.r-right-zchlnj{right:0px;}
|
||||
.r-top-ipm5af{top:0px;}"
|
||||
`;
|
||||
@@ -68,7 +68,7 @@ describe.each([['concurrent'], ['legacy']])('AppRegistry', (mode) => {
|
||||
expect(setMountedState).toHaveBeenLastCalledWith(false);
|
||||
});
|
||||
|
||||
test('styles roots in different documents', () => {
|
||||
test('styles roots in iframes', () => {
|
||||
AppRegistry.registerComponent('App', () => NoopComponent);
|
||||
act(() => {
|
||||
AppRegistry.runApplication('App', { initialProps: {}, rootTag, mode });
|
||||
@@ -76,7 +76,6 @@ describe.each([['concurrent'], ['legacy']])('AppRegistry', (mode) => {
|
||||
// Create iframe context
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
const iframeRootTag = document.createElement('div');
|
||||
iframeRootTag.id = 'react-iframe-root';
|
||||
iframe.contentWindow.document.body.appendChild(iframeRootTag);
|
||||
@@ -90,43 +89,39 @@ describe.each([['concurrent'], ['legacy']])('AppRegistry', (mode) => {
|
||||
mode
|
||||
});
|
||||
});
|
||||
|
||||
const iframedoc = iframeRootTag.ownerDocument;
|
||||
expect(iframedoc).toBe(iframe.contentWindow.document);
|
||||
expect(iframedoc).not.toBe(document);
|
||||
const cssText = iframedoc.getElementById(
|
||||
'react-native-stylesheet'
|
||||
).textContent;
|
||||
expect(cssText).toMatchSnapshot('iframe css');
|
||||
});
|
||||
|
||||
const cssText = Array.prototype.slice
|
||||
.call(
|
||||
iframedoc.getElementById('react-native-stylesheet').sheet.cssRules
|
||||
)
|
||||
.map((cssRule) => cssRule.cssText);
|
||||
test('styles roots in shadow trees', () => {
|
||||
AppRegistry.registerComponent('App', () => NoopComponent);
|
||||
act(() => {
|
||||
AppRegistry.runApplication('App', { initialProps: {}, rootTag, mode });
|
||||
});
|
||||
// Create shadow dom
|
||||
const shadowRootHost = document.createElement('div');
|
||||
const shadowRoot = shadowRootHost.attachShadow({ mode: 'open' });
|
||||
const shadowContainer = document.createElement('div');
|
||||
shadowRoot.appendChild(shadowContainer);
|
||||
document.body.appendChild(shadowRootHost);
|
||||
|
||||
expect(cssText).toMatchInlineSnapshot(`
|
||||
[
|
||||
"[stylesheet-group=\\"0\\"] {}",
|
||||
"body {margin: 0;}",
|
||||
"button::-moz-focus-inner,input::-moz-focus-inner {border: 0; padding: 0;}",
|
||||
"html {-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: rgba(0,0,0,0);}",
|
||||
"input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration {display: none;}",
|
||||
"[stylesheet-group=\\"1\\"] {}",
|
||||
".css-view-175oi2r {align-items: stretch; background-color: rgba(0,0,0,0.00); border: 0 solid black; box-sizing: border-box; display: flex; flex-basis: auto; flex-direction: column; flex-shrink: 0; list-style: none; margin: 0px; min-height: 0px; min-width: 0px; padding: 0px; position: relative; text-decoration: none; z-index: 0;}",
|
||||
"[stylesheet-group=\\"2\\"] {}",
|
||||
".r-display-xoduu5 {display: inline-flex;}",
|
||||
".r-flex-13awgt0 {flex: 1;}",
|
||||
"[stylesheet-group=\\"3\\"] {}",
|
||||
".r-bottom-1p0dtai {bottom: 0px;}",
|
||||
".r-left-1d2f490 {left: 0px;}",
|
||||
".r-pointerEvents-105ug2t {pointer-events: auto !important;}",
|
||||
".r-pointerEvents-12vffkv>* {pointer-events: auto;}",
|
||||
".r-pointerEvents-12vffkv {pointer-events: none !important;}",
|
||||
".r-pointerEvents-633pao {pointer-events: none !important;}",
|
||||
".r-pointerEvents-ah5dr5>* {pointer-events: none;}",
|
||||
".r-pointerEvents-ah5dr5 {pointer-events: auto !important;}",
|
||||
".r-position-u8s1d {position: absolute;}",
|
||||
".r-right-zchlnj {right: 0px;}",
|
||||
".r-top-ipm5af {top: 0px;}",
|
||||
]
|
||||
`);
|
||||
// Run in shadow dom
|
||||
act(() => {
|
||||
AppRegistry.runApplication('App', {
|
||||
initialProps: {},
|
||||
rootTag: shadowContainer,
|
||||
mode
|
||||
});
|
||||
});
|
||||
const cssText = shadowRoot.getElementById(
|
||||
'react-native-stylesheet'
|
||||
).textContent;
|
||||
expect(cssText).toMatchSnapshot('shadow dom css');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { ComponentType, Node } from 'react';
|
||||
|
||||
import AppContainer from './AppContainer';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import renderLegacy, { hydrateLegacy, render, hydrate } from '../render';
|
||||
import render, { hydrateLegacy, renderLegacy, hydrate } from '../render';
|
||||
import StyleSheet from '../StyleSheet';
|
||||
import React from 'react';
|
||||
|
||||
@@ -32,6 +32,7 @@ export default function renderApplication<Props: Object>(
|
||||
}
|
||||
): Application {
|
||||
const { hydrate: shouldHydrate, initialProps, mode, rootTag } = options;
|
||||
|
||||
const renderFn = shouldHydrate
|
||||
? mode === 'concurrent'
|
||||
? hydrate
|
||||
|
||||
@@ -9,14 +9,14 @@ import { createSheet } from '../dom';
|
||||
|
||||
describe('createSheet', () => {
|
||||
test('creates a sheet on the client', () => {
|
||||
const sheet = createSheet();
|
||||
const sheet = createSheet(document);
|
||||
expect(sheet.id).toMatchInlineSnapshot(`"react-native-stylesheet"`);
|
||||
expect(typeof sheet.getTextContent()).toBe('string');
|
||||
expect(typeof sheet.insert).toBe('function');
|
||||
});
|
||||
|
||||
test('supports multiple documents with same styles', () => {
|
||||
const sheet = createSheet();
|
||||
const sheet = createSheet(document);
|
||||
sheet.insert('.test-sheet { opacity: 1 }', 3);
|
||||
|
||||
// Iframe -----
|
||||
@@ -25,7 +25,7 @@ describe('createSheet', () => {
|
||||
const iframeDoc = iframe.contentWindow.document;
|
||||
const iframeRootTag = document.createElement('div');
|
||||
iframeDoc.body.appendChild(iframeRootTag);
|
||||
const iframeSheet = createSheet(iframeRootTag);
|
||||
const iframeSheet = createSheet(iframeDoc);
|
||||
|
||||
// Did we generate a new sheet?
|
||||
expect(sheet).not.toBe(iframeSheet);
|
||||
@@ -39,20 +39,22 @@ describe('createSheet', () => {
|
||||
expect(iframeSheet.getTextContent().includes('test-iframe')).toBe(true);
|
||||
|
||||
// ShadowDOM -----
|
||||
const shadowRootHost = document.createElement('div');
|
||||
const shadowRoot = shadowRootHost.attachShadow({ mode: 'open' });
|
||||
const div = document.createElement('div');
|
||||
const shadowRoot = div.attachShadow({ mode: 'open' });
|
||||
const shadowRootTag = document.createElement('div');
|
||||
shadowRoot.appendChild(shadowRootTag);
|
||||
document.body.appendChild(shadowRoot);
|
||||
const shadowSheet = createSheet(shadowRootTag);
|
||||
shadowRoot.appendChild(div);
|
||||
document.body.appendChild(shadowRootHost);
|
||||
const shadowSheet = createSheet(shadowRoot);
|
||||
|
||||
// Did we generate a new sheet?
|
||||
expect(sheet).not.toBe(shadowSheet);
|
||||
expect(shadowSheet.id).toMatchInlineSnapshot(`"react-native-stylesheet"`);
|
||||
expect(typeof shadowSheet.insert).toBe('function');
|
||||
// expect(shadowRoot.getElementById('react-native-stylesheet')).not.toBe(null);
|
||||
expect(shadowRoot.getElementById('react-native-stylesheet')).not.toBe(null);
|
||||
// Does the content match existing sheets?
|
||||
expect(shadowSheet.getTextContent().includes('test-sheet')).toBe(true);
|
||||
// NOTE: commented out because JSDOM doesn't reproduce the inserted text content.
|
||||
// JSDOM doesn't appear to populate `sheet.cssRules` after a sheet's `textContent` is set.
|
||||
// expect(shadowSheet.getTextContent().includes('test-sheet')).toBe(true);
|
||||
// Does the content update when other sheets are updated?
|
||||
sheet.insert('.test-shadow { opacity: 0 }', 3);
|
||||
expect(shadowSheet.getTextContent().includes('test-shadow')).toBe(true);
|
||||
|
||||
@@ -14,19 +14,18 @@ export default function createCSSStyleSheet(
|
||||
rootNode?: Document | ShadowRoot,
|
||||
textContent?: string
|
||||
): ?CSSStyleSheet {
|
||||
if (canUseDOM) {
|
||||
const root = rootNode != null ? rootNode : document;
|
||||
let element = root.getElementById(id);
|
||||
if (canUseDOM && rootNode != null) {
|
||||
let element = rootNode.getElementById(id);
|
||||
if (element == null) {
|
||||
element = document.createElement('style');
|
||||
element.setAttribute('id', id);
|
||||
if (typeof textContent === 'string') {
|
||||
element.appendChild(document.createTextNode(textContent));
|
||||
}
|
||||
if (root instanceof ShadowRoot) {
|
||||
root.insertBefore(element, root.firstChild);
|
||||
if (rootNode instanceof ShadowRoot) {
|
||||
rootNode.insertBefore(element, rootNode.firstChild);
|
||||
} else {
|
||||
const head = root.head;
|
||||
const head = rootNode.head;
|
||||
if (head) {
|
||||
head.insertBefore(element, head.firstChild);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ type Sheet = {
|
||||
};
|
||||
|
||||
const defaultId = 'react-native-stylesheet';
|
||||
const roots = new WeakMap<Node, number>();
|
||||
const sheets = [];
|
||||
const _roots = new WeakMap<Node, number>();
|
||||
const _sheets = [];
|
||||
|
||||
const initialRules = [
|
||||
// minimal top-level reset
|
||||
@@ -31,16 +31,25 @@ const initialRules = [
|
||||
];
|
||||
|
||||
export function createSheet(
|
||||
root?: HTMLElement,
|
||||
_rootNode?: Document | ShadowRoot,
|
||||
id?: string = defaultId
|
||||
): Sheet {
|
||||
let sheet;
|
||||
|
||||
if (canUseDOM) {
|
||||
const rootNode: Node = root != null ? root.getRootNode() : document;
|
||||
window.__sheets = window.__sheets || _sheets;
|
||||
window.__roots = window.__roots || _roots;
|
||||
|
||||
const sheets = window.__sheets;
|
||||
const roots = window.__roots;
|
||||
let rootNode = _rootNode;
|
||||
|
||||
if (_rootNode == null) {
|
||||
rootNode = document;
|
||||
}
|
||||
// Create the initial style sheet
|
||||
if (sheets.length === 0) {
|
||||
sheet = createOrderedCSSStyleSheet(createCSSStyleSheet(id));
|
||||
sheet = createOrderedCSSStyleSheet(createCSSStyleSheet(id, rootNode));
|
||||
initialRules.forEach((rule) => {
|
||||
sheet.insert(rule, 0);
|
||||
});
|
||||
@@ -65,14 +74,14 @@ export function createSheet(
|
||||
}
|
||||
} else {
|
||||
// Create the initial style sheet
|
||||
if (sheets.length === 0) {
|
||||
if (_sheets.length === 0) {
|
||||
sheet = createOrderedCSSStyleSheet(createCSSStyleSheet(id));
|
||||
initialRules.forEach((rule) => {
|
||||
sheet.insert(rule, 0);
|
||||
});
|
||||
sheets.push(sheet);
|
||||
_sheets.push(sheet);
|
||||
} else {
|
||||
sheet = sheets[0];
|
||||
sheet = _sheets[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +91,7 @@ export function createSheet(
|
||||
},
|
||||
id,
|
||||
insert(cssText: string, groupValue: number) {
|
||||
const sheets = canUseDOM ? window.__sheets : _sheets;
|
||||
sheets.forEach((s) => {
|
||||
s.insert(cssText, groupValue);
|
||||
});
|
||||
|
||||
@@ -19,34 +19,38 @@ import {
|
||||
import unmountComponentAtNode from '../unmountComponentAtNode';
|
||||
import { createSheet } from '../StyleSheet/dom';
|
||||
|
||||
export function hydrate(element, root) {
|
||||
createSheet(root);
|
||||
return domHydrateRoot(root, element);
|
||||
export function hydrate(element, container) {
|
||||
const rootNode = container.getRootNode();
|
||||
createSheet(rootNode);
|
||||
return domHydrateRoot(container, element);
|
||||
}
|
||||
|
||||
export function render(element, root) {
|
||||
createSheet(root);
|
||||
const reactRoot = domCreateRoot(root);
|
||||
export default function render(element, container) {
|
||||
const rootNode = container.getRootNode();
|
||||
const reactRoot = domCreateRoot(container);
|
||||
reactRoot.render(element);
|
||||
createSheet(rootNode);
|
||||
return reactRoot;
|
||||
}
|
||||
|
||||
export function hydrateLegacy(element, root, callback) {
|
||||
createSheet(root);
|
||||
domLegacyHydrate(element, root, callback);
|
||||
export function hydrateLegacy(element, container, callback) {
|
||||
const rootNode = container.getRootNode();
|
||||
domLegacyHydrate(element, container, callback);
|
||||
createSheet(rootNode);
|
||||
return {
|
||||
unmount: function () {
|
||||
return unmountComponentAtNode(root);
|
||||
return unmountComponentAtNode(container);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default function renderLegacy(element, root, callback) {
|
||||
createSheet(root);
|
||||
domLegacyRender(element, root, callback);
|
||||
export function renderLegacy(element, container, callback) {
|
||||
const rootNode = container.getRootNode();
|
||||
domLegacyRender(element, container, callback);
|
||||
createSheet(rootNode);
|
||||
return {
|
||||
unmount: function () {
|
||||
return unmountComponentAtNode(root);
|
||||
return unmountComponentAtNode(container);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user