/** * ✔ nodes */ import { View, ViewProps, ViewStyle } from 'react-native' import { useRef, forwardRef, JSX, useState, createElement } from 'react' import useInnerProps from '../getInnerListeners' import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数 import { useTransformStyle, useLayout, extendObject } from '../utils' import { WebView, WebViewMessageEvent } from 'react-native-webview' import { generateHTML } from './html' import Portal from '../mpx-portal' type Node = { type: 'node' | 'text' name?: string attrs?: any children?: Array text: string } interface _RichTextProps extends ViewProps { style?: ViewStyle nodes: string | Array 'enable-var'?: boolean 'external-var-context'?: Record 'parent-font-size'?: number 'parent-width'?: number 'parent-height'?: number } function jsonToHtmlStr (elements: Array) { let htmlStr = '' for (const element of elements) { if (element.type === 'text') { htmlStr += element.text return htmlStr } const { name, attrs = {}, children = [] } = element let attrStr = '' for (const [key, value] of Object.entries(attrs)) attrStr += ` ${key}="${value}"` let childrenStr = '' for (const child of children) childrenStr += jsonToHtmlStr([child]) htmlStr += `<${name}${attrStr}>${childrenStr}` } return htmlStr } const _RichText = forwardRef, _RichTextProps>((props, ref): JSX.Element => { const { style = {}, nodes, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props const nodeRef = useRef(null) const [webViewHeight, setWebViewHeight] = useState(0) const { normalStyle, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(Object.assign({ width: '100%', height: webViewHeight }, style), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }) const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef }) useNodesRef(props, ref, nodeRef, { layoutRef }) const innerProps = useInnerProps( extendObject( {}, props, layoutProps, { ref: nodeRef, style: extendObject(normalStyle, layoutStyle) } ), [], { layoutRef } ) const html: string = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes) let finalComponent: JSX.Element = createElement(View, innerProps, createElement(WebView, { source: { html: generateHTML(html) }, onMessage: (event: WebViewMessageEvent) => { setWebViewHeight(+event.nativeEvent.data) }, style: { backgroundColor: 'transparent' } }) ) if (hasPositionFixed) { finalComponent = createElement(Portal, null, finalComponent) } return finalComponent }) _RichText.displayName = 'MpxRichText' export default _RichText