-
-
Notifications
You must be signed in to change notification settings - Fork 591
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new
TRenderEngineProvider
and RenderHTMLFragment
components
These components are meant to be used together in order to avoid re-instantiating transient render engines for each instance of `RenderHTML`, which is a costy operation when performed many times. This is especially useful for apps using many instances, such as chat apps. For this to work, mount a `TRenderEngineProvider` component near the root of your application, and pass all it all props it support (see typescript definition files for that purpose). Down in the render tree, mount as many `RenderHTMLFragment` components as you wish, they will all share the same `TRenderEngine` instance.
- Loading branch information
Showing
13 changed files
with
396 additions
and
242 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,202 +1,25 @@ | ||
import React, { useMemo } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Dimensions, Platform } from 'react-native'; | ||
import { RenderResolvedHTMLProps, RenderHTMLProps } from './shared-types'; | ||
import useTTree from './hooks/useTTree'; | ||
import SharedPropsContext, { | ||
defaultSharedPropsContext | ||
} from './context/SharedPropsContext'; | ||
import TChildrenRenderersContext from './context/TChildrenRendererContext'; | ||
import TNodeChildrenRenderer from './TNodeChildrenRenderer'; | ||
import RenderHTMLDebug from './RenderHTMLDebug'; | ||
import SourceLoader from './SourceLoader'; | ||
import RenderRegistryProvider from './context/RenderRegistryProvider'; | ||
import TChildrenRenderer from './TChildrenRenderer'; | ||
import TDocumentRenderer from './TDocumentRenderer'; | ||
import React from 'react'; | ||
|
||
export type RenderHTMLPropTypes = Record<keyof RenderHTMLProps, any>; | ||
import { RenderHTMLProps } from './shared-types'; | ||
|
||
const propTypes: RenderHTMLPropTypes = { | ||
renderers: PropTypes.object.isRequired, | ||
defaultTextProps: PropTypes.object, | ||
defaultViewProps: PropTypes.object, | ||
source: PropTypes.oneOfType([ | ||
PropTypes.shape({ | ||
html: PropTypes.string.isRequired, | ||
baseUrl: PropTypes.string | ||
}), | ||
PropTypes.shape({ | ||
uri: PropTypes.string.isRequired, | ||
method: PropTypes.string, | ||
body: PropTypes.any, | ||
headers: PropTypes.object | ||
}) | ||
]), | ||
enableCSSInlineProcessing: PropTypes.bool, | ||
enableUserAgentStyles: PropTypes.bool, | ||
enableExperimentalMarginCollapsing: PropTypes.bool, | ||
idsStyles: PropTypes.object, | ||
remoteErrorView: PropTypes.func, | ||
remoteLoadingView: PropTypes.func, | ||
ignoredTags: PropTypes.array.isRequired, | ||
ignoredStyles: PropTypes.array.isRequired, | ||
allowedStyles: PropTypes.array, | ||
htmlParserOptions: PropTypes.object, | ||
debug: PropTypes.bool.isRequired, | ||
listsPrefixesRenderers: PropTypes.object, | ||
ignoreDOMNode: PropTypes.func, | ||
alterDOMData: PropTypes.func, | ||
alterDOMChildren: PropTypes.func, | ||
alterDOMElement: PropTypes.func, | ||
tagsStyles: PropTypes.object, | ||
classesStyles: PropTypes.object, | ||
onLinkPress: PropTypes.func, | ||
computeEmbeddedMaxWidth: PropTypes.func, | ||
contentWidth: PropTypes.number, | ||
enableExperimentalPercentWidth: PropTypes.bool, | ||
imagesInitialDimensions: PropTypes.shape({ | ||
width: PropTypes.number, | ||
height: PropTypes.number | ||
}), | ||
emSize: PropTypes.number.isRequired, | ||
baseStyle: PropTypes.object, | ||
renderersProps: PropTypes.object, | ||
onTTreeChange: PropTypes.func, | ||
onHTMLLoaded: PropTypes.func, | ||
systemFonts: PropTypes.arrayOf(PropTypes.string), | ||
fallbackFonts: PropTypes.shape({ | ||
serif: PropTypes.string, | ||
'sans-serif': PropTypes.string, | ||
monospace: PropTypes.string | ||
}), | ||
triggerTREInvalidationPropNames: PropTypes.arrayOf(PropTypes.string), | ||
WebView: PropTypes.any, | ||
defaultWebViewProps: PropTypes.object, | ||
onDocumentMetadataLoaded: PropTypes.func | ||
}; | ||
import TRenderEngineProvider from './TRenderEngineProvider'; | ||
import RenderHTMLFragment from './RenderHTMLFragment'; | ||
|
||
const defaultProps: { | ||
[k in keyof RenderHTMLProps]?: RenderHTMLProps[k]; | ||
} = { | ||
...defaultSharedPropsContext, | ||
htmlParserOptions: { | ||
decodeEntities: true | ||
}, | ||
emSize: 14, | ||
ignoredTags: [], | ||
ignoredStyles: [], | ||
baseStyle: { fontSize: 14 }, | ||
tagsStyles: {}, | ||
classesStyles: {}, | ||
enableUserAgentStyles: true, | ||
enableCSSInlineProcessing: true, | ||
renderers: {}, | ||
fallbackFonts: { | ||
'sans-serif': Platform.select({ ios: 'system', default: 'sans-serif' }), | ||
monospace: Platform.select({ ios: 'Menlo', default: 'monospace' }), | ||
serif: Platform.select({ ios: 'Times New Roman', default: 'serif' }) | ||
}, | ||
systemFonts: Platform.select({ | ||
default: [], | ||
ios: [ | ||
'San Francisco', | ||
'Arial', | ||
'ArialHebrew', | ||
'Avenir', | ||
'Baskerville', | ||
'Bodoni 72', | ||
'Bradley Hand', | ||
'Chalkboard SE', | ||
'Cochin', | ||
'Copperplate', | ||
'Courier', | ||
'Courier New', | ||
'Damascus', | ||
'Didot', | ||
'Futura', | ||
'Geeza Pro', | ||
'Georgia', | ||
'Gill Sans', | ||
'Helvetica', | ||
'Helvetica Neue', | ||
'Hiragino Sans', | ||
'Hoefler Text', | ||
'Iowan Old Style', | ||
'Kailasa', | ||
'Khmer Sangam MN', | ||
'Marker Felt', | ||
'Menlo', | ||
'Mishafi', | ||
'Noteworthy', | ||
'Optima', | ||
'Palatino', | ||
'Papyrus', | ||
'Savoye LET', | ||
'Symbol', | ||
'Thonburi', | ||
'Times New Roman', | ||
'Trebuchet MS', | ||
'Verdana', | ||
'Zapf Dingbats', | ||
'Zapfino' | ||
], | ||
android: [ | ||
'Roboto', | ||
'notoserif', | ||
'sans-serif-light', | ||
'sans-serif-thin', | ||
'sans-serif-medium' | ||
] | ||
}), | ||
triggerTREInvalidationPropNames: [], | ||
debug: __DEV__, | ||
contentWidth: undefined | ||
}; | ||
|
||
function RenderResolvedHTML(props: RenderResolvedHTMLProps) { | ||
const ttree = useTTree(props); | ||
return ( | ||
<TDocumentRenderer | ||
tdoc={ttree} | ||
baseUrl={props.baseUrl} | ||
onDocumentMetadataLoaded={props.onDocumentMetadataLoaded} | ||
/> | ||
); | ||
} | ||
|
||
export default function RenderHTML({ | ||
defaultTextProps, | ||
...props | ||
}: RenderHTMLProps) { | ||
const normalizedProps = { | ||
contentWidth: Dimensions.get('window').width, | ||
...props, | ||
defaultTextProps: { ...defaultProps.defaultTextProps, ...defaultTextProps } | ||
}; | ||
/** | ||
* Render HTML text in native views! | ||
* | ||
* @remarks - If your application uses many instances of this component, you | ||
* should share the render engine across those instances via the | ||
* `TRenderEngineProvier` component, and render the HTML with | ||
* `RenderHTMLFragment` instead. That should significantly increase | ||
* performance. | ||
* | ||
* @param props - Props for this component. | ||
*/ | ||
export default function RenderHTML(props: RenderHTMLProps) { | ||
return ( | ||
<RenderHTMLDebug {...props}> | ||
<RenderRegistryProvider renderers={normalizedProps.renderers}> | ||
<SharedPropsContext.Provider | ||
value={normalizedProps as Required<RenderHTMLProps>}> | ||
<TChildrenRenderersContext.Provider | ||
value={useMemo( | ||
() => ({ | ||
TChildrenRenderer, | ||
TNodeChildrenRenderer | ||
}), | ||
[] | ||
)}> | ||
<SourceLoader {...normalizedProps}> | ||
{(resolvedProps) => ( | ||
<RenderResolvedHTML {...normalizedProps} {...resolvedProps} /> | ||
)} | ||
</SourceLoader> | ||
</TChildrenRenderersContext.Provider> | ||
</SharedPropsContext.Provider> | ||
</RenderRegistryProvider> | ||
</RenderHTMLDebug> | ||
<TRenderEngineProvider {...props}> | ||
{React.createElement(RenderHTMLFragment, props)} | ||
</TRenderEngineProvider> | ||
); | ||
} | ||
|
||
RenderHTML.defaultProps = defaultProps; | ||
RenderHTML.propTypes = propTypes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import React, { useMemo } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { | ||
RenderResolvedHTMLProps, | ||
ResolvedResourceProps, | ||
RenderHTMLFragmentProps | ||
} from './shared-types'; | ||
import useTTree from './hooks/useTTree'; | ||
import SharedPropsContext, { | ||
defaultSharedPropsContext | ||
} from './context/SharedPropsContext'; | ||
import TChildrenRenderersContext from './context/TChildrenRendererContext'; | ||
import TNodeChildrenRenderer from './TNodeChildrenRenderer'; | ||
import RenderHTMLFragmentDebug from './RenderHTMLFragmentDebug'; | ||
import SourceLoader from './SourceLoader'; | ||
import TChildrenRenderer from './TChildrenRenderer'; | ||
import TDocumentRenderer from './TDocumentRenderer'; | ||
import selectSharedProps from './helpers/selectSharedProps'; | ||
|
||
export type RenderHTMLFragmentPropTypes = Record< | ||
keyof RenderHTMLFragmentProps, | ||
any | ||
>; | ||
|
||
export const renderHtmlFragmentPropTypes: RenderHTMLFragmentPropTypes = { | ||
defaultTextProps: PropTypes.object, | ||
defaultViewProps: PropTypes.object, | ||
source: PropTypes.oneOfType([ | ||
PropTypes.shape({ | ||
html: PropTypes.string.isRequired, | ||
baseUrl: PropTypes.string | ||
}), | ||
PropTypes.shape({ | ||
uri: PropTypes.string.isRequired, | ||
method: PropTypes.string, | ||
body: PropTypes.any, | ||
headers: PropTypes.object | ||
}) | ||
]), | ||
enableExperimentalMarginCollapsing: PropTypes.bool, | ||
remoteErrorView: PropTypes.func, | ||
remoteLoadingView: PropTypes.func, | ||
debug: PropTypes.bool.isRequired, | ||
listsPrefixesRenderers: PropTypes.object, | ||
onLinkPress: PropTypes.func, | ||
computeEmbeddedMaxWidth: PropTypes.func, | ||
contentWidth: PropTypes.number, | ||
enableExperimentalPercentWidth: PropTypes.bool, | ||
imagesInitialDimensions: PropTypes.shape({ | ||
width: PropTypes.number, | ||
height: PropTypes.number | ||
}), | ||
renderersProps: PropTypes.object, | ||
onTTreeChange: PropTypes.func, | ||
onHTMLLoaded: PropTypes.func, | ||
WebView: PropTypes.any, | ||
defaultWebViewProps: PropTypes.object | ||
}; | ||
|
||
export const renderHTMLFragmentDefaultProps: { | ||
[k in keyof RenderHTMLFragmentProps]?: RenderHTMLFragmentProps[k]; | ||
} = { | ||
...defaultSharedPropsContext, | ||
contentWidth: undefined | ||
}; | ||
|
||
function RenderResolvedHTML(props: RenderResolvedHTMLProps) { | ||
const ttree = useTTree(props); | ||
return ( | ||
<TDocumentRenderer | ||
tdoc={ttree} | ||
baseUrl={props.baseUrl} | ||
onDocumentMetadataLoaded={props.onDocumentMetadataLoaded} | ||
/> | ||
); | ||
} | ||
|
||
const renderResolved = (resolvedProps: ResolvedResourceProps) => ( | ||
<RenderResolvedHTML {...resolvedProps} /> | ||
); | ||
|
||
/** | ||
* Render a HTML snippet, given that there is a `TRenderEngineProvider` up in | ||
* the render tree. | ||
* | ||
* @param props - Props for this component. | ||
*/ | ||
export default function RenderHTMLFragment(props: RenderHTMLFragmentProps) { | ||
const { | ||
source, | ||
onHTMLLoaded, | ||
remoteErrorView, | ||
remoteLoadingView, | ||
...remainingProps | ||
} = props; | ||
const sourceLoaderProps = { | ||
source, | ||
onHTMLLoaded, | ||
remoteErrorView, | ||
remoteLoadingView, | ||
children: renderResolved | ||
}; | ||
return ( | ||
<RenderHTMLFragmentDebug {...props}> | ||
<SharedPropsContext.Provider value={selectSharedProps(remainingProps)}> | ||
<TChildrenRenderersContext.Provider | ||
value={useMemo( | ||
() => ({ | ||
TChildrenRenderer, | ||
TNodeChildrenRenderer | ||
}), | ||
[] | ||
)}> | ||
{React.createElement(SourceLoader, sourceLoaderProps)} | ||
</TChildrenRenderersContext.Provider> | ||
</SharedPropsContext.Provider> | ||
</RenderHTMLFragmentDebug> | ||
); | ||
} | ||
|
||
RenderHTMLFragment.defaultProps = renderHTMLFragmentDefaultProps; | ||
RenderHTMLFragment.propTypes = renderHtmlFragmentPropTypes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.