diff --git a/packages/render-html/src/RenderHTML.tsx b/packages/render-html/src/RenderHTML.tsx index beffc370c..41c95818b 100644 --- a/packages/render-html/src/RenderHTML.tsx +++ b/packages/render-html/src/RenderHTML.tsx @@ -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; +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 ( - - ); -} - -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 ( - - - }> - ({ - TChildrenRenderer, - TNodeChildrenRenderer - }), - [] - )}> - - {(resolvedProps) => ( - - )} - - - - - + + {React.createElement(RenderHTMLFragment, props)} + ); } - -RenderHTML.defaultProps = defaultProps; -RenderHTML.propTypes = propTypes; diff --git a/packages/render-html/src/RenderHTMLFragment.tsx b/packages/render-html/src/RenderHTMLFragment.tsx new file mode 100644 index 000000000..ed477cf64 --- /dev/null +++ b/packages/render-html/src/RenderHTMLFragment.tsx @@ -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 ( + + ); +} + +const renderResolved = (resolvedProps: ResolvedResourceProps) => ( + +); + +/** + * 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 ( + + + ({ + TChildrenRenderer, + TNodeChildrenRenderer + }), + [] + )}> + {React.createElement(SourceLoader, sourceLoaderProps)} + + + + ); +} + +RenderHTMLFragment.defaultProps = renderHTMLFragmentDefaultProps; +RenderHTMLFragment.propTypes = renderHtmlFragmentPropTypes; diff --git a/packages/render-html/src/RenderHTMLDebug.tsx b/packages/render-html/src/RenderHTMLFragmentDebug.tsx similarity index 82% rename from packages/render-html/src/RenderHTMLDebug.tsx rename to packages/render-html/src/RenderHTMLFragmentDebug.tsx index aa8479a94..7bfbfba58 100644 --- a/packages/render-html/src/RenderHTMLDebug.tsx +++ b/packages/render-html/src/RenderHTMLFragmentDebug.tsx @@ -1,11 +1,7 @@ import React, { Fragment } from 'react'; import { PropsWithChildren } from 'react'; import lookupRecord from './helpers/lookupRecord'; -import { RenderHTMLProps } from './shared-types'; - -export function RenderHTMLProd(props: PropsWithChildren) { - return {props.children}; -} +import { RenderHTMLFragmentProps } from './shared-types'; export const messages = { outdatedUriProp: @@ -25,8 +21,8 @@ export const messages = { 'https://reactnative.dev/docs/usewindowdimensions' }; -const RenderHTMLDebug = function RenderHTMLDebug( - props: PropsWithChildren +const RenderHTMLFragmentDebug = function RenderHTMLDebug( + props: PropsWithChildren ) { if (__DEV__) { if (typeof props.contentWidth !== 'number') { @@ -45,4 +41,4 @@ const RenderHTMLDebug = function RenderHTMLDebug( return {props.children}; }; -export default RenderHTMLDebug; +export default RenderHTMLFragmentDebug; diff --git a/packages/render-html/src/TRenderEngineProvider.tsx b/packages/render-html/src/TRenderEngineProvider.tsx new file mode 100644 index 000000000..9a0daa7d3 --- /dev/null +++ b/packages/render-html/src/TRenderEngineProvider.tsx @@ -0,0 +1,149 @@ +import TRenderEngine from '@native-html/transient-render-engine'; +import React, { PropsWithChildren } from 'react'; +import { Platform } from 'react-native'; +import PropTypes from 'prop-types'; +import useTRenderEngine from './hooks/useTRenderEngine'; +import { TransientRenderEngineConfig } from './shared-types'; +import RenderRegistryProvider from './context/RenderRegistryProvider'; + +const defaultTRenderEngine = {} as any; + +const TRenderEngineContext = React.createContext( + defaultTRenderEngine +); + +export const tRenderEngineProviderPropTypes: Record< + keyof TransientRenderEngineConfig, + any +> = { + renderers: PropTypes.object.isRequired, + enableCSSInlineProcessing: PropTypes.bool, + enableUserAgentStyles: PropTypes.bool, + idsStyles: PropTypes.object, + ignoredTags: PropTypes.array.isRequired, + ignoredStyles: PropTypes.array.isRequired, + allowedStyles: PropTypes.array, + htmlParserOptions: PropTypes.object, + ignoreDOMNode: PropTypes.func, + alterDOMData: PropTypes.func, + alterDOMChildren: PropTypes.func, + alterDOMElement: PropTypes.func, + tagsStyles: PropTypes.object, + classesStyles: PropTypes.object, + emSize: PropTypes.number.isRequired, + baseStyle: PropTypes.object, + systemFonts: PropTypes.arrayOf(PropTypes.string), + fallbackFonts: PropTypes.shape({ + serif: PropTypes.string, + 'sans-serif': PropTypes.string, + monospace: PropTypes.string + }), + triggerTREInvalidationPropNames: PropTypes.arrayOf(PropTypes.string), + onDocumentMetadataLoaded: PropTypes.func +}; + +export const defaultTRenderEngineProviderProps: TransientRenderEngineConfig = { + 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: [] +}; + +export function useAmbiantTRenderEngine() { + const engine = React.useContext(TRenderEngineContext); + if (__DEV__ && engine === defaultTRenderEngine) { + console.error('TRenderEngineProvider is missing in the render tree.'); + } + return engine; +} + +/** + * A react component to share a transient web engine instance across different + * rendered contents via `RenderHTMLFragment`. This can seriously enhance + * performance in applications with potentially dozens or hundreds of distinct + * rendered snippets such as chat apps. + * + * @param props - Pass engine config here. + */ +export default function TRenderEngineProvider({ + children, + ...config +}: PropsWithChildren) { + const engine = useTRenderEngine(config); + return ( + + + {children} + + + ); +} + +TRenderEngineProvider.defaultProps = defaultTRenderEngineProviderProps; +TRenderEngineProvider.propTypes = tRenderEngineProviderPropTypes; diff --git a/packages/render-html/src/__tests__/component.render-html-dev.test.tsx b/packages/render-html/src/__tests__/component.render-html-fragment-debug.test.tsx similarity index 75% rename from packages/render-html/src/__tests__/component.render-html-dev.test.tsx rename to packages/render-html/src/__tests__/component.render-html-fragment-debug.test.tsx index 034f136fb..91d733c78 100644 --- a/packages/render-html/src/__tests__/component.render-html-dev.test.tsx +++ b/packages/render-html/src/__tests__/component.render-html-fragment-debug.test.tsx @@ -1,30 +1,33 @@ import React from 'react'; import { render } from 'react-native-testing-library'; -import RenderHTMLDebug, { messages } from '../RenderHTMLDebug'; +import RenderHTMLFragmentDebug, { messages } from '../RenderHTMLFragmentDebug'; beforeAll(function () { //@ts-expect-error global.__DEV__ = true; }); -describe('RenderHTMLDebug', () => { +describe('RenderHTMLFragmentDebug', () => { it('should warn when contentWidth has not been provided', () => { console.warn = jest.fn(); render( - Hello world

' }} debug={false} /> + Hello world

' }} + debug={false} + /> ); expect(console.warn).toHaveBeenNthCalledWith(1, messages.contentWidth); }); it('should warn when source has not been provided', () => { console.warn = jest.fn(); //@ts-expect-error - render(); + render(); expect(console.warn).toHaveBeenNthCalledWith(1, messages.noSource); }); it('should warn when outdated html prop has been provided', () => { console.warn = jest.fn(); render( - React.createElement(RenderHTMLDebug, { + React.createElement(RenderHTMLFragmentDebug, { contentWidth: 0, source: { html: 'hello world' }, //@ts-expect-error @@ -37,7 +40,7 @@ describe('RenderHTMLDebug', () => { it('should warn when outdated uri prop has been provided', () => { console.warn = jest.fn(); render( - React.createElement(RenderHTMLDebug, { + React.createElement(RenderHTMLFragmentDebug, { contentWidth: 0, source: { html: 'hello world' }, //@ts-expect-error diff --git a/packages/render-html/src/__tests__/component.render-html.test.tsx b/packages/render-html/src/__tests__/component.render-html.test.tsx index e81e3bde6..8b8d6c61d 100644 --- a/packages/render-html/src/__tests__/component.render-html.test.tsx +++ b/packages/render-html/src/__tests__/component.render-html.test.tsx @@ -12,6 +12,7 @@ describe('RenderHTML', () => { ).not.toThrow(); }); it('should render without error when missing a source', () => { + //@ts-expect-error expect(() => render()).not.toThrow(); }); it('should update ImgTag contentWidth when contentWidth prop changes', () => { diff --git a/packages/render-html/src/context/SharedPropsContext.ts b/packages/render-html/src/context/SharedPropsContext.ts index 5cd20e45c..4f426ce5b 100644 --- a/packages/render-html/src/context/SharedPropsContext.ts +++ b/packages/render-html/src/context/SharedPropsContext.ts @@ -1,5 +1,5 @@ import React, { useCallback } from 'react'; -import { Dimensions, Linking, TextProps } from 'react-native'; +import { Dimensions, Linking, TextProps, ViewProps } from 'react-native'; import { RenderHTMLPassedProps } from '../shared-types'; export const defaultSharedPropsContext: Required = { @@ -19,7 +19,15 @@ export const defaultSharedPropsContext: Required = { }, listsPrefixesRenderers: {}, onLinkPress: (_e, href) => Linking.canOpenURL(href) && Linking.openURL(href), - WebView: () => null, + WebView: () => { + if (__DEV__) { + console.warn( + 'One of your renderer is attempting to use WebView component, which has not been ' + + "provided as a prop to the RenderHtml component. As a consequence, the element won't be rendered." + ); + } + return null; + }, defaultWebViewProps: {}, renderersProps: {} }; @@ -41,11 +49,17 @@ export function useRendererProps< } export function useDefaultTextProps(): TextProps { - return useSharedProps().defaultTextProps; + return { + ...defaultSharedPropsContext.defaultTextProps, + ...useSharedProps().defaultTextProps + }; } -export function useDefaultViewProps(): TextProps { - return useSharedProps().defaultViewProps; +export function useDefaultViewProps(): ViewProps { + return { + ...defaultSharedPropsContext.defaultViewProps, + ...useSharedProps().defaultViewProps + }; } export function useComputeMaxWidthForTag(tagName: string) { diff --git a/packages/render-html/src/helpers/__tests__/selectSharedProps.test.ts b/packages/render-html/src/helpers/__tests__/selectSharedProps.test.ts new file mode 100644 index 000000000..0b0a28fa5 --- /dev/null +++ b/packages/render-html/src/helpers/__tests__/selectSharedProps.test.ts @@ -0,0 +1,12 @@ +import selectSharedProps from '../selectSharedProps'; + +describe('selectSharedProps', () => { + it('should default to default values', () => { + expect(selectSharedProps({ contentWidth: undefined }).contentWidth).toEqual( + expect.any(Number) + ); + }); + it('should retain non-nil values', () => { + expect(selectSharedProps({ contentWidth: 300 }).contentWidth).toEqual(300); + }); +}); diff --git a/packages/render-html/src/helpers/selectSharedProps.ts b/packages/render-html/src/helpers/selectSharedProps.ts new file mode 100644 index 000000000..64eb7a3e2 --- /dev/null +++ b/packages/render-html/src/helpers/selectSharedProps.ts @@ -0,0 +1,16 @@ +import pickBy from 'ramda/src/pickBy'; +import pick from 'ramda/src/pick'; +import pipe from 'ramda/src/pipe'; +import mergeRight from 'ramda/src/mergeRight'; +import { RenderHTMLPassedProps, RenderHTMLProps } from '../shared-types'; +import { defaultSharedPropsContext } from '../context/SharedPropsContext'; + +const selectSharedProps: ( + props: Partial +) => Required = pipe( + pick(Object.keys(defaultSharedPropsContext)), + pickBy((val) => val != null), + mergeRight(defaultSharedPropsContext) as any +); + +export default selectSharedProps; diff --git a/packages/render-html/src/hooks/useTRenderEngine.ts b/packages/render-html/src/hooks/useTRenderEngine.ts index 5e4c2d019..c391b5c01 100644 --- a/packages/render-html/src/hooks/useTRenderEngine.ts +++ b/packages/render-html/src/hooks/useTRenderEngine.ts @@ -6,11 +6,11 @@ import TRenderEngine, { TagName, DOMElement } from '@native-html/transient-render-engine'; -import { RenderResolvedHTMLProps } from '../shared-types'; +import { TransientRenderEngineConfig } from '../shared-types'; import { CustomRendererSpecs } from '../render/render-types'; import lookupRecord from '../helpers/lookupRecord'; -export default function useTRenderEngine(props: RenderResolvedHTMLProps) { +export default function useTRenderEngine(props: TransientRenderEngineConfig) { const { allowedStyles, ignoredStyles, diff --git a/packages/render-html/src/hooks/useTTree.ts b/packages/render-html/src/hooks/useTTree.ts index 8bbcf1830..172594812 100644 --- a/packages/render-html/src/hooks/useTTree.ts +++ b/packages/render-html/src/hooks/useTTree.ts @@ -1,10 +1,10 @@ import { tnodeToString } from '@native-html/transient-render-engine'; import { useMemo, useEffect } from 'react'; import { RenderResolvedHTMLProps } from '../shared-types'; -import useTRenderEngine from './useTRenderEngine'; +import { useAmbiantTRenderEngine } from '../TRenderEngineProvider'; export default function useTTree(props: RenderResolvedHTMLProps) { - const trenderEngine = useTRenderEngine(props); + const trenderEngine = useAmbiantTRenderEngine(); const ttree = useMemo(() => trenderEngine.buildTTree(props.html), [ props.html, trenderEngine diff --git a/packages/render-html/src/index.ts b/packages/render-html/src/index.ts index e70057fc0..0040c824a 100644 --- a/packages/render-html/src/index.ts +++ b/packages/render-html/src/index.ts @@ -50,6 +50,8 @@ export { } from './TNodeChildrenRenderer'; export type { TNodeChildrenRendererProps } from './TNodeChildrenRenderer'; export { default as TNodeRenderer } from './TNodeRenderer'; +export { default as TRenderEngineProvider } from './TRenderEngineProvider'; +export { default as RenderHTMLFragment } from './RenderHTMLFragment'; export { default as useInternalRenderer } from './hooks/useInternalRenderer'; export { default as useNormalizedUrl } from './hooks/useNormalizedUrl'; export { default as useTRenderEngine } from './hooks/useTRenderEngine'; diff --git a/packages/render-html/src/shared-types.ts b/packages/render-html/src/shared-types.ts index 4cdab6e53..353822613 100644 --- a/packages/render-html/src/shared-types.ts +++ b/packages/render-html/src/shared-types.ts @@ -250,6 +250,29 @@ export interface TransientRenderEngineConfig { * The default value in pixels for 1em */ emSize?: number; + /** + * Name of props which should trigger a rebuild of the Transient Render + * Engine (TRE). + * + * @remarks For performance sake, this component will not recreate an + * instance of the engine on each prop change. If you need some props to + * trigger a new instantiation, such as `tagsStyles`, pass a list of their + * names in this array. + * + * Please note that only props involved in the building of the transient render + * tree are concerned by this mechanism. + * + * @example + * ```ts + * triggerTREInvalidationPropNames = ['tagsStyles', 'allowedStyles'] + * ``` + */ + triggerTREInvalidationPropNames?: Array< + Exclude< + keyof TransientRenderEngineConfig, + 'triggerTREInvalidationPropNames' + > + >; } export interface RenderHTMLSourceUri { @@ -286,9 +309,8 @@ export interface RenderHTMLSourceInline { export type RenderHTMLSource = RenderHTMLSourceInline | RenderHTMLSourceUri; -export interface RenderHTMLProps

- extends RenderHTMLPassedProps

, - TransientRenderEngineConfig { +export interface RenderHTMLFragmentProps

+ extends RenderHTMLPassedProps

{ /** * The object source to render (either `{ uri }` or `{ html }`). */ @@ -309,27 +331,16 @@ export interface RenderHTMLProps

* Triggered when HTML is available to the RenderHTML component. */ onHTMLLoaded?: (html: string) => void; - /** - * Name of props which should trigger a rebuild of the Transient Render - * Engine (TRE). - * - * @remarks For performance sake, this component will not recreate an - * instance of the engine on each prop change. If you need some props to - * trigger a new instantiation, such as `tagsStyles`, pass a list of their - * names in this array. - * - * Please note that only props involved in the building of the transient render - * tree are concerned by this mechanism. - * - * @example - * ```ts - * triggerTREInvalidationPropNames = ['tagsStyles', 'allowedStyles'] - * ``` - */ - triggerTREInvalidationPropNames?: Array; } -export type RenderResolvedHTMLProps = Omit & { +export interface RenderHTMLProps

+ extends RenderHTMLFragmentProps

, + TransientRenderEngineConfig {} + +export type RenderResolvedHTMLProps = Pick< + RenderHTMLProps, + 'onTTreeChange' | 'debug' +> & { html: string; baseUrl?: string; onDocumentMetadataLoaded?: TransientRenderEngineConfig['onDocumentMetadataLoaded']; @@ -340,7 +351,11 @@ export interface ResolvedResourceProps { baseUrl?: string; } -export interface SourceLoaderProps extends RenderHTMLProps { +export interface SourceLoaderProps + extends Pick< + RenderHTMLProps, + 'source' | 'remoteLoadingView' | 'remoteErrorView' | 'onHTMLLoaded' + > { children: (resource: ResolvedResourceProps) => ReactElement; }