import Document , { Annotation } from "@atjson/document" ; import Renderer , { classify } from "@atjson/renderer-hir" ; import * as React from "react" ; import { ComponentType , FC , ReactElement } from "react" ; export type AttributesOf < AnnotationClass > = AnnotationClass extends Annotation < infer Attributes > ? { [ P in keyof Attributes ] : Attributes [ P ] extends Document ? React . ReactFragment : Attributes [ P ] ; } : never ; const EMPTY_COMPONENT_MAP = { } ; export const ReactRendererContext = React . createContext < { [ key ] : ComponentType < any > ; } > ( EMPTY_COMPONENT_MAP ) ; export const ReactRendererConsumer = ReactRendererContext . Consumer ; export const ReactRendererProvider = ( { children , value } ) => { return React . createElement ( ReactRendererConsumer , null , ( parentComponentMap ) => { const mergedValues = { ... parentComponentMap , ... value } ; return React . createElement ( ReactRendererContext . Provider , { value : mergedValues } , children ) ; } ) ; } ; export default class ReactRenderer extends Renderer { * root ( ) { return React . createElement ( React . Fragment , { } , ... ( yield ) ) ; } renderSubdocuments ( annotation ) { const annotationConstructor = annotation . getAnnotationConstructor ( ) ; if ( ! annotationConstructor . subdocuments ) { return ; } for ( let subdocKey in annotationConstructor . subdocuments ) { if ( ! ( subdocKey in annotation . attributes ) ) { continue ; } annotation . attributes [ subdocKey ] = ReactRenderer . render ( annotation . attributes [ subdocKey ] ) ; } } * renderAnnotation ( annotation ) { this . renderSubdocuments ( annotation ) ; const annotationChildren = yield ; const key = `${ annotation . id }-${ annotation . start }` ; return React . createElement ( ReactRendererConsumer , { key , children : ( componentMap ) => { if ( componentMap === EMPTY_COMPONENT_MAP ) { throw new Error ( "Component map is empty. Did you wrap your render call in ReactRendererProvider?" ) ; } let AnnotationComponent = componentMap [ annotation . type ] || componentMap [ classify ( annotation . type ) ] ; if ( AnnotationComponent ) { return React . createElement ( AnnotationComponent , annotation . attributes , ... annotationChildren ) ; } else { return annotationChildren ; } } , } ) ; } }