@flexiformal/ftml-react 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/leptos.tsx"],"sourcesContent":["import * as Base from \"@flexiformal/ftml\";\nexport * as FTML from \"@flexiformal/ftml\";\nimport { ReactNode, useContext, useEffect, useRef } from \"react\";\nimport {\n useLeptosTunnel,\n useLeptosTunnels,\n ReactLeptosContext,\n toConfig,\n} from \"./leptos\";\n\nexport const initialize = Base.initialize;\n\nexport function getCurrentUri(): Base.FTML.NarrativeUri | undefined {\n const context = useContext(ReactLeptosContext);\n if (context) {\n return Base.FTML.get_current_uri(context);\n }\n}\n\nexport interface FtmlConfig {\n allowHovers?: boolean;\n allowFullscreen?: boolean;\n allowFormalInfo?: boolean;\n allowNotationChanges?: boolean;\n chooseHighlightStyle?: boolean;\n showContent?: boolean;\n pdfLink?: boolean;\n highlightStyle?: Base.FTML.HighlightStyle;\n toc?: Base.FTML.TocSource;\n autoexpandLimit?: Base.FTML.LogicalLevel;\n tocProgress?: Base.FTML.TocProgress[];\n sectionWrap?: (\n uri: Base.FTML.DocumentElementUri,\n lvl: Base.FTML.SectionLevel,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n paragraphWrap?: (\n uri: Base.FTML.DocumentElementUri,\n kind: Base.FTML.ParagraphKind,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n slideWrap?: (\n uri: Base.FTML.DocumentElementUri,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n problemWrap?: (\n uri: Base.FTML.DocumentElementUri,\n subProblem: boolean,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n onSectionTitle?: (\n uri: Base.FTML.DocumentElementUri,\n lvl: Base.FTML.SectionLevel,\n ) => ReactNode | undefined;\n problemStates?: Base.FTML.ProblemStates;\n onProblemResponse?: (r: Base.FTML.ProblemResponse) => void;\n}\n\nexport interface FTMLSetupArgs extends FtmlConfig {\n children: ReactNode;\n}\n\n/**\n * Sets up Leptos' reactive system\n */\nexport const FTMLSetup: React.FC<FTMLSetupArgs> = (args) => {\n const mountRef = useRef<HTMLDivElement>(null);\n const main = useLeptosTunnel();\n const { addTunnel, TunnelRenderer } = useLeptosTunnels();\n const context = useContext(ReactLeptosContext);\n\n useEffect(() => {\n if (!mountRef.current) return;\n if (context) {\n const handle = Base.FTML.apply_config(\n toConfig(args, addTunnel),\n mountRef.current,\n context,\n );\n return () => {\n handle.unmount();\n };\n } else {\n const handle = Base.ftmlSetup(\n mountRef.current,\n (e, o) => {\n main.addTunnel(\n e,\n <>\n {args.children}\n <TunnelRenderer />\n </>,\n o,\n );\n },\n toConfig(args, addTunnel),\n );\n return () => {\n handle.unmount();\n };\n }\n }, []);\n\n return (\n <>\n <div ref={mountRef} style={{ display: \"contents\" }} />\n <main.TunnelRenderer />\n </>\n );\n};\n\n/**\n * See {@link FTMLConfig} and {@link FTML.DocumentOptions}\n */\nexport interface FTMLDocumentArgs extends FtmlConfig {\n document: Base.FTML.DocumentOptions;\n}\n\n/**\n * render an FTML document\n */\nexport const FTMLDocument: React.FC<FTMLDocumentArgs> = (args) => {\n const mountRef = useRef<HTMLDivElement>(null);\n const { addTunnel, TunnelRenderer } = useLeptosTunnels();\n const context = useContext(ReactLeptosContext);\n\n useEffect(() => {\n if (mountRef.current === null) return;\n const handle = Base.ftmlDocument(\n mountRef.current,\n args.document,\n context,\n toConfig(args, addTunnel),\n );\n return () => {\n handle.unmount();\n };\n }, []);\n return (\n <div style={{ textAlign: \"start\" }}>\n <div ref={mountRef} />\n <TunnelRenderer />\n </div>\n );\n};\n\n/**\n * See {@link FTMLConfig} and {@link Base.FTML.FragmentOptions}\n */\nexport interface FTMLFragmentArgs extends FtmlConfig {\n fragment: Base.FTML.FragmentOptions;\n}\n\n/**\n * render an FTML fragment\n */\nexport const FTMLFragment: React.FC<FTMLFragmentArgs> = (args) => {\n const mountRef = useRef<HTMLDivElement>(null);\n const { addTunnel, TunnelRenderer } = useLeptosTunnels();\n const context = useContext(ReactLeptosContext);\n\n useEffect(() => {\n if (!mountRef.current) return;\n const handle = Base.ftmlFragment(\n mountRef.current,\n args.fragment,\n context,\n toConfig(args, addTunnel),\n );\n return () => {\n handle.unmount();\n };\n }, []);\n return (\n <div style={{ textAlign: \"start\" }}>\n <div ref={mountRef} />\n <TunnelRenderer />\n </div>\n );\n};\n","import { createContext, ReactNode, useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { FTML } from \"@flexiformal/ftml\";\nimport { FtmlConfig } from \".\";\n\ntype LeptosContext = FTML.LeptosContext;\n\nexport const ReactLeptosContext = createContext<LeptosContext | undefined>(\n undefined,\n);\ninterface Tunnel {\n element: Element;\n node: ReactNode;\n context: LeptosContext;\n id: string; // for React keys\n}\n\nexport function toConfig(\n config: FtmlConfig,\n addTunnel: (\n element: Element,\n node: ReactNode,\n context: FTML.LeptosContext,\n ) => string,\n): FTML.FtmlConfig {\n let cfg: FTML.FtmlConfig = {\n allowHovers: config.allowHovers,\n allowFullscreen: config.allowFullscreen,\n allowFormalInfo: config.allowFormalInfo,\n allowNotationChanges: config.allowNotationChanges,\n chooseHighlightStyle: config.chooseHighlightStyle,\n showContent: config.showContent,\n pdfLink: config.pdfLink,\n toc: config.toc,\n tocProgress: config.tocProgress,\n autoexpandLimit: config.autoexpandLimit,\n onProblemResponse: config.onProblemResponse,\n problemStates: config.problemStates,\n };\n const ost = config.onSectionTitle;\n if (ost) {\n cfg.onSectionTitle = (u, lvl) => {\n return (e, ctx) => {\n addTunnel(e, ost(u, lvl), ctx);\n };\n };\n }\n const sect = config.sectionWrap;\n if (sect) {\n cfg.sectionWrap = (u, lvl) => {\n return (e, ctx) => {\n const r = sect(u, lvl);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n const para = config.paragraphWrap;\n if (para) {\n cfg.paragraphWrap = (u, knd) => {\n return (e, ctx) => {\n const r = para(u, knd);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n const slide = config.slideWrap;\n if (slide) {\n cfg.slideWrap = (u) => {\n return (e, ctx) => {\n const r = slide(u);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n const problem = config.problemWrap;\n if (problem) {\n cfg.problemWrap = (u, sub) => {\n return (e, ctx) => {\n const r = problem(u, sub);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n return cfg;\n}\n\nexport function elemToReact(\n elem: HTMLDivElement,\n ctx: FTML.LeptosContext,\n): ReactNode {\n const chs = Array.from(elem.childNodes);\n chs.forEach((c) => elem.removeChild(c));\n return <ElemToReact elems={chs} ctx={ctx} />;\n}\n\nconst ElemToReact: React.FC<{\n elems: ChildNode[];\n ctx: FTML.LeptosContext;\n}> = ({ elems, ctx }) => {\n const ref = useRef<HTMLDivElement>(null);\n const done = useRef<boolean>(false);\n useEffect(() => {\n if (ref.current && !done.current) {\n done.current = true;\n ref.current.replaceChildren(...elems);\n }\n }, []);\n return (\n <ReactLeptosContext.Provider value={ctx}>\n <div ref={ref} style={{ display: \"contents\" }} />\n </ReactLeptosContext.Provider>\n );\n};\n\nexport function useLeptosTunnel() {\n const [tunnel, setTunnel] = useState<Tunnel | undefined>(undefined);\n\n const addTunnel = (\n element: Element,\n node: ReactNode,\n context: LeptosContext,\n ) => {\n const id = Math.random().toString(36).slice(2);\n setTunnel({ element, node, id, context });\n return id; // Return id for later removal\n };\n\n const removeTunnel = () => {\n setTunnel(undefined);\n };\n\n const TunnelRenderer = () =>\n tunnel ? (\n createPortal(\n <ReactLeptosContext.Provider value={tunnel.context}>\n {tunnel.node}\n </ReactLeptosContext.Provider>,\n tunnel.element,\n tunnel.id,\n )\n ) : (\n <></>\n );\n\n useEffect(() => {\n return () => {\n if (tunnel) {\n //try{tunnel.context.cleanup();} catch (e){console.log(\"Error cleaning up leptos context:\",e)}\n }\n };\n });\n\n return {\n addTunnel,\n removeTunnel,\n TunnelRenderer,\n };\n}\n\nexport function useLeptosTunnels() {\n const [tunnels, setTunnels] = useState<Tunnel[]>([]);\n\n const addTunnel = (\n element: Element,\n node: ReactNode,\n context: LeptosContext,\n ) => {\n const id = Math.random().toString(36).slice(2);\n setTunnels((prev) => [...prev, { element, node, id, context }]);\n return id; // Return id for later removal\n };\n\n const removeTunnel = (id: string) => {\n setTunnels((prev) =>\n prev.filter((tunnel) => {\n if (tunnel.id === id) {\n //try{tunnel.context.cleanup();} catch (e){console.log(\"Error cleaning up leptos context:\",e)}\n }\n return tunnel.id !== id;\n }),\n );\n };\n\n const TunnelRenderer = () => (\n <>\n {tunnels.map((tunnel) =>\n createPortal(\n <ReactLeptosContext.Provider value={tunnel.context}>\n {tunnel.node}\n </ReactLeptosContext.Provider>,\n tunnel.element,\n tunnel.id,\n ),\n )}\n </>\n );\n\n useEffect(() => {\n return () => {\n tunnels.forEach((tunnel) => {\n //try{tunnel.context.cleanup();} catch (e){console.log(\"Error cleaning up leptos context:\",e)}\n });\n };\n });\n\n return {\n addTunnel,\n removeTunnel,\n TunnelRenderer,\n };\n}\n"],"mappings":";AAAA,YAAY,UAAU;AACtB,YAAYA,WAAU;AACtB,SAAoB,YAAY,aAAAC,YAAW,UAAAC,eAAc;;;ACFzD,SAAS,eAA0B,WAAW,QAAQ,gBAAgB;AACtE,SAAS,oBAAoB;AAmGlB,SAiDC,UAjDD;AA7FJ,IAAM,qBAAqB;AAAA,EAC9B;AACJ;AAQO,SAAS,SACZ,QACA,WAKe;AACf,MAAI,MAAuB;AAAA,IACvB,aAAa,OAAO;AAAA,IACpB,iBAAiB,OAAO;AAAA,IACxB,iBAAiB,OAAO;AAAA,IACxB,sBAAsB,OAAO;AAAA,IAC7B,sBAAsB,OAAO;AAAA,IAC7B,aAAa,OAAO;AAAA,IACpB,SAAS,OAAO;AAAA,IAChB,KAAK,OAAO;AAAA,IACZ,aAAa,OAAO;AAAA,IACpB,iBAAiB,OAAO;AAAA,IACxB,mBAAmB,OAAO;AAAA,IAC1B,eAAe,OAAO;AAAA,EAC1B;AACA,QAAM,MAAM,OAAO;AACnB,MAAI,KAAK;AACL,QAAI,iBAAiB,CAAC,GAAG,QAAQ;AAC7B,aAAO,CAAC,GAAG,QAAQ;AACf,kBAAU,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG;AAAA,MACjC;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,OAAO,OAAO;AACpB,MAAI,MAAM;AACN,QAAI,cAAc,CAAC,GAAG,QAAQ;AAC1B,aAAO,CAAC,GAAG,QAAQ;AACf,cAAM,IAAI,KAAK,GAAG,GAAG;AACrB,YAAI,GAAG;AACH,oBAAU,GAAG,EAAE,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;AAAA,QAC5C;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,OAAO,OAAO;AACpB,MAAI,MAAM;AACN,QAAI,gBAAgB,CAAC,GAAG,QAAQ;AAC5B,aAAO,CAAC,GAAG,QAAQ;AACf,cAAM,IAAI,KAAK,GAAG,GAAG;AACrB,YAAI,GAAG;AACH,oBAAU,GAAG,EAAE,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;AAAA,QAC5C;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,QAAQ,OAAO;AACrB,MAAI,OAAO;AACP,QAAI,YAAY,CAAC,MAAM;AACnB,aAAO,CAAC,GAAG,QAAQ;AACf,cAAM,IAAI,MAAM,CAAC;AACjB,YAAI,GAAG;AACH,oBAAU,GAAG,EAAE,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;AAAA,QAC5C;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,UAAU,OAAO;AACvB,MAAI,SAAS;AACT,QAAI,cAAc,CAAC,GAAG,QAAQ;AAC1B,aAAO,CAAC,GAAG,QAAQ;AACf,cAAM,IAAI,QAAQ,GAAG,GAAG;AACxB,YAAI,GAAG;AACH,oBAAU,GAAG,EAAE,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;AAAA,QAC5C;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAEO,SAAS,YACZ,MACA,KACS;AACT,QAAM,MAAM,MAAM,KAAK,KAAK,UAAU;AACtC,MAAI,QAAQ,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;AACtC,SAAO,oBAAC,eAAY,OAAO,KAAK,KAAU;AAC9C;AAEA,IAAM,cAGD,CAAC,EAAE,OAAO,IAAI,MAAM;AACrB,QAAM,MAAM,OAAuB,IAAI;AACvC,QAAM,OAAO,OAAgB,KAAK;AAClC,YAAU,MAAM;AACZ,QAAI,IAAI,WAAW,CAAC,KAAK,SAAS;AAC9B,WAAK,UAAU;AACf,UAAI,QAAQ,gBAAgB,GAAG,KAAK;AAAA,IACxC;AAAA,EACJ,GAAG,CAAC,CAAC;AACL,SACI,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,KAChC,8BAAC,SAAI,KAAU,OAAO,EAAE,SAAS,WAAW,GAAG,GACnD;AAER;AAEO,SAAS,kBAAkB;AAC9B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA6B,MAAS;AAElE,QAAM,YAAY,CACd,SACA,MACA,YACC;AACD,UAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AAC7C,cAAU,EAAE,SAAS,MAAM,IAAI,QAAQ,CAAC;AACxC,WAAO;AAAA,EACX;AAEA,QAAM,eAAe,MAAM;AACvB,cAAU,MAAS;AAAA,EACvB;AAEA,QAAM,iBAAiB,MACnB,SACI;AAAA,IACI,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,OAAO,SACtC,iBAAO,MACZ;AAAA,IACA,OAAO;AAAA,IACP,OAAO;AAAA,EACX,IAEA,gCAAE;AAGV,YAAU,MAAM;AACZ,WAAO,MAAM;AACT,UAAI,QAAQ;AAAA,MAEZ;AAAA,IACJ;AAAA,EACJ,CAAC;AAED,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;AAEO,SAAS,mBAAmB;AAC/B,QAAM,CAAC,SAAS,UAAU,IAAI,SAAmB,CAAC,CAAC;AAEnD,QAAM,YAAY,CACd,SACA,MACA,YACC;AACD,UAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AAC7C,eAAW,CAAC,SAAS,CAAC,GAAG,MAAM,EAAE,SAAS,MAAM,IAAI,QAAQ,CAAC,CAAC;AAC9D,WAAO;AAAA,EACX;AAEA,QAAM,eAAe,CAAC,OAAe;AACjC;AAAA,MAAW,CAAC,SACR,KAAK,OAAO,CAAC,WAAW;AACpB,YAAI,OAAO,OAAO,IAAI;AAAA,QAEtB;AACA,eAAO,OAAO,OAAO;AAAA,MACzB,CAAC;AAAA,IACL;AAAA,EACJ;AAEA,QAAM,iBAAiB,MACnB,gCACK,kBAAQ;AAAA,IAAI,CAAC,WACV;AAAA,MACI,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,OAAO,SACtC,iBAAO,MACZ;AAAA,MACA,OAAO;AAAA,MACP,OAAO;AAAA,IACX;AAAA,EACJ,GACJ;AAGJ,YAAU,MAAM;AACZ,WAAO,MAAM;AACT,cAAQ,QAAQ,CAAC,WAAW;AAAA,MAE5B,CAAC;AAAA,IACL;AAAA,EACJ,CAAC;AAED,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;;;ADtIwB,qBAAAC,WAEI,OAAAC,MAFJ;AA1EjB,IAAMC,cAAkB;AAExB,SAAS,gBAAoD;AAChE,QAAM,UAAU,WAAW,kBAAkB;AAC7C,MAAI,SAAS;AACT,WAAY,UAAK,gBAAgB,OAAO;AAAA,EAC5C;AACJ;AA4CO,IAAM,YAAqC,CAAC,SAAS;AACxD,QAAM,WAAWC,QAAuB,IAAI;AAC5C,QAAM,OAAO,gBAAgB;AAC7B,QAAM,EAAE,WAAW,eAAe,IAAI,iBAAiB;AACvD,QAAM,UAAU,WAAW,kBAAkB;AAE7C,EAAAC,WAAU,MAAM;AACZ,QAAI,CAAC,SAAS,QAAS;AACvB,QAAI,SAAS;AACT,YAAM,SAAc,UAAK;AAAA,QACrB,SAAS,MAAM,SAAS;AAAA,QACxB,SAAS;AAAA,QACT;AAAA,MACJ;AACA,aAAO,MAAM;AACT,eAAO,QAAQ;AAAA,MACnB;AAAA,IACJ,OAAO;AACH,YAAM,SAAc;AAAA,QAChB,SAAS;AAAA,QACT,CAAC,GAAG,MAAM;AACN,eAAK;AAAA,YACD;AAAA,YACA,qBAAAJ,WAAA,EACK;AAAA,mBAAK;AAAA,cACN,gBAAAC,KAAC,kBAAe;AAAA,eACpB;AAAA,YACA;AAAA,UACJ;AAAA,QACJ;AAAA,QACA,SAAS,MAAM,SAAS;AAAA,MAC5B;AACA,aAAO,MAAM;AACT,eAAO,QAAQ;AAAA,MACnB;AAAA,IACJ;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,SACI,qBAAAD,WAAA,EACI;AAAA,oBAAAC,KAAC,SAAI,KAAK,UAAU,OAAO,EAAE,SAAS,WAAW,GAAG;AAAA,IACpD,gBAAAA,KAAC,KAAK,gBAAL,EAAoB;AAAA,KACzB;AAER;AAYO,IAAM,eAA2C,CAAC,SAAS;AAC9D,QAAM,WAAWE,QAAuB,IAAI;AAC5C,QAAM,EAAE,WAAW,eAAe,IAAI,iBAAiB;AACvD,QAAM,UAAU,WAAW,kBAAkB;AAE7C,EAAAC,WAAU,MAAM;AACZ,QAAI,SAAS,YAAY,KAAM;AAC/B,UAAM,SAAc;AAAA,MAChB,SAAS;AAAA,MACT,KAAK;AAAA,MACL;AAAA,MACA,SAAS,MAAM,SAAS;AAAA,IAC5B;AACA,WAAO,MAAM;AACT,aAAO,QAAQ;AAAA,IACnB;AAAA,EACJ,GAAG,CAAC,CAAC;AACL,SACI,qBAAC,SAAI,OAAO,EAAE,WAAW,QAAQ,GAC7B;AAAA,oBAAAH,KAAC,SAAI,KAAK,UAAU;AAAA,IACpB,gBAAAA,KAAC,kBAAe;AAAA,KACpB;AAER;AAYO,IAAM,eAA2C,CAAC,SAAS;AAC9D,QAAM,WAAWE,QAAuB,IAAI;AAC5C,QAAM,EAAE,WAAW,eAAe,IAAI,iBAAiB;AACvD,QAAM,UAAU,WAAW,kBAAkB;AAE7C,EAAAC,WAAU,MAAM;AACZ,QAAI,CAAC,SAAS,QAAS;AACvB,UAAM,SAAc;AAAA,MAChB,SAAS;AAAA,MACT,KAAK;AAAA,MACL;AAAA,MACA,SAAS,MAAM,SAAS;AAAA,IAC5B;AACA,WAAO,MAAM;AACT,aAAO,QAAQ;AAAA,IACnB;AAAA,EACJ,GAAG,CAAC,CAAC;AACL,SACI,qBAAC,SAAI,OAAO,EAAE,WAAW,QAAQ,GAC7B;AAAA,oBAAAH,KAAC,SAAI,KAAK,UAAU;AAAA,IACpB,gBAAAA,KAAC,kBAAe;AAAA,KACpB;AAER;","names":["FTML","useEffect","useRef","Fragment","jsx","initialize","useRef","useEffect"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/leptos.tsx"],"sourcesContent":["import * as Base from \"@flexiformal/ftml\";\nexport * as FTML from \"@flexiformal/ftml\";\nimport { ReactNode, useContext, useEffect, useRef } from \"react\";\nimport {\n useLeptosTunnel,\n useLeptosTunnels,\n ReactLeptosContext,\n toConfig,\n} from \"./leptos\";\n\nexport const initialize = Base.initialize;\n\nexport function getCurrentUri(): Base.FTML.NarrativeUri | undefined {\n const context = useContext(ReactLeptosContext);\n if (context) {\n return Base.FTML.get_current_uri(context);\n }\n}\n\nexport interface FtmlConfig {\n allowHovers?: boolean;\n allowFullscreen?: boolean;\n allowFormalInfo?: boolean;\n allowNotationChanges?: boolean;\n chooseHighlightStyle?: boolean;\n showContent?: boolean;\n pdfLink?: boolean;\n highlightStyle?: Base.FTML.HighlightStyle;\n toc?: Base.FTML.TocSource;\n autoexpandLimit?: Base.FTML.LogicalLevel;\n tocProgress?: Base.FTML.TocProgress[];\n sectionWrap?: (\n uri: Base.FTML.DocumentElementUri,\n lvl: Base.FTML.SectionLevel,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n paragraphWrap?: (\n uri: Base.FTML.DocumentElementUri,\n kind: Base.FTML.ParagraphKind,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n slideWrap?: (\n uri: Base.FTML.DocumentElementUri,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n problemWrap?: (\n uri: Base.FTML.DocumentElementUri,\n subProblem: boolean,\n ) => ((ch: ReactNode) => ReactNode) | undefined;\n onSectionTitle?: (\n uri: Base.FTML.DocumentElementUri,\n lvl: Base.FTML.SectionLevel,\n ) => ReactNode | undefined;\n problemStates?: Base.FTML.ProblemStates;\n onProblemResponse?: (r: Base.FTML.ProblemResponse) => void;\n}\n\nexport interface FTMLSetupArgs extends FtmlConfig {\n children: ReactNode;\n}\n\n/**\n * Sets up Leptos' reactive system\n */\nexport const FTMLSetup: React.FC<FTMLSetupArgs> = (args) => {\n const mountRef = useRef<HTMLDivElement>(null);\n const main = useLeptosTunnel();\n const { addTunnel, TunnelRenderer } = useLeptosTunnels();\n const context = useContext(ReactLeptosContext);\n\n useEffect(() => {\n if (!mountRef.current) return;\n if (context) {\n const handle = Base.FTML.apply_config(\n toConfig(args, addTunnel),\n mountRef.current,\n context,\n );\n return () => {\n handle.unmount();\n };\n } else {\n const handle = Base.ftmlSetup(\n mountRef.current,\n (e, o) => {\n main.addTunnel(\n e,\n <>\n {args.children}\n <TunnelRenderer />\n </>,\n o,\n );\n },\n toConfig(args, addTunnel),\n );\n return () => {\n handle.unmount();\n };\n }\n }, []);\n\n return (\n <>\n <div ref={mountRef} style={{ display: \"contents\" }} />\n <main.TunnelRenderer />\n </>\n );\n};\n\n/**\n * See {@link FTMLConfig} and {@link FTML.DocumentOptions}\n */\nexport interface FTMLDocumentArgs extends FtmlConfig {\n document: Base.FTML.DocumentOptions;\n}\n\n/**\n * render an FTML document\n */\nexport const FTMLDocument: React.FC<FTMLDocumentArgs> = (args) => {\n const mountRef = useRef<HTMLDivElement>(null);\n const { addTunnel, TunnelRenderer } = useLeptosTunnels();\n const context = useContext(ReactLeptosContext);\n\n useEffect(() => {\n if (mountRef.current === null) return;\n const handle = Base.ftmlDocument(\n mountRef.current,\n args.document,\n context,\n toConfig(args, addTunnel),\n );\n return () => {\n handle.unmount();\n };\n }, []);\n return (\n <div style={{ textAlign: \"start\" }}>\n <div ref={mountRef} />\n <TunnelRenderer />\n </div>\n );\n};\n\n/**\n * See {@link FTMLConfig} and {@link Base.FTML.FragmentOptions}\n */\nexport interface FTMLFragmentArgs extends FtmlConfig {\n fragment: Base.FTML.FragmentOptions;\n}\n\n/**\n * render an FTML fragment\n */\nexport const FTMLFragment: React.FC<FTMLFragmentArgs> = (args) => {\n const mountRef = useRef<HTMLDivElement | null>(null);\n const { addTunnel, TunnelRenderer } = useLeptosTunnels();\n const context = useContext(ReactLeptosContext);\n\n useEffect(() => {\n if (!mountRef.current) return;\n //console.log(\"Mounting fragment \",args.fragment.uri);\n const handle = Base.ftmlFragment(\n mountRef.current,\n args.fragment,\n context,\n toConfig(args, addTunnel),\n );\n return () => {\n //console.log(\"Unmounting fragment \",args.fragment.uri);\n handle.unmount();\n };\n }, []);\n return (\n <div style={{ textAlign: \"start\" }}>\n <div ref={mountRef} />\n <TunnelRenderer />\n </div>\n );\n};\n","import { createContext, ReactNode, useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { FTML } from \"@flexiformal/ftml\";\nimport { FtmlConfig } from \".\";\n\ntype LeptosContext = FTML.LeptosContext;\n\nexport const ReactLeptosContext = createContext<LeptosContext | undefined>(\n undefined,\n);\ninterface Tunnel {\n element: Element;\n node: ReactNode;\n context: LeptosContext;\n id: string; // for React keys\n}\n\nexport function toConfig(\n config: FtmlConfig,\n addTunnel: (\n element: Element,\n node: ReactNode,\n context: FTML.LeptosContext,\n ) => string,\n): FTML.FtmlConfig {\n let cfg: FTML.FtmlConfig = {\n allowHovers: config.allowHovers,\n allowFullscreen: config.allowFullscreen,\n allowFormalInfo: config.allowFormalInfo,\n allowNotationChanges: config.allowNotationChanges,\n chooseHighlightStyle: config.chooseHighlightStyle,\n showContent: config.showContent,\n pdfLink: config.pdfLink,\n toc: config.toc,\n tocProgress: config.tocProgress,\n autoexpandLimit: config.autoexpandLimit,\n onProblemResponse: config.onProblemResponse,\n problemStates: config.problemStates,\n };\n const ost = config.onSectionTitle;\n if (ost) {\n cfg.onSectionTitle = (u, lvl) => {\n return (e, ctx) => {\n addTunnel(e, ost(u, lvl), ctx);\n };\n };\n }\n const sect = config.sectionWrap;\n if (sect) {\n cfg.sectionWrap = (u, lvl) => {\n return (e, ctx) => {\n const r = sect(u, lvl);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n const para = config.paragraphWrap;\n if (para) {\n cfg.paragraphWrap = (u, knd) => {\n return (e, ctx) => {\n const r = para(u, knd);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n const slide = config.slideWrap;\n if (slide) {\n cfg.slideWrap = (u) => {\n return (e, ctx) => {\n const r = slide(u);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n const problem = config.problemWrap;\n if (problem) {\n cfg.problemWrap = (u, sub) => {\n return (e, ctx) => {\n const r = problem(u, sub);\n if (r) {\n addTunnel(e, r(elemToReact(e, ctx)), ctx);\n }\n };\n };\n }\n return cfg;\n}\n\nexport function elemToReact(\n elem: HTMLDivElement,\n ctx: FTML.LeptosContext,\n): ReactNode {\n const chs = Array.from(elem.childNodes);\n chs.forEach((c) => elem.removeChild(c));\n return <ElemToReact elems={chs} ctx={ctx} />;\n}\n\nconst ElemToReact: React.FC<{\n elems: ChildNode[];\n ctx: FTML.LeptosContext;\n}> = ({ elems, ctx }) => {\n const ref = useRef<HTMLDivElement>(null);\n const done = useRef<boolean>(false);\n useEffect(() => {\n if (ref.current && !done.current) {\n done.current = true;\n ref.current.replaceChildren(...elems);\n }\n }, []);\n return (\n <ReactLeptosContext.Provider value={ctx}>\n <div ref={ref} style={{ display: \"contents\" }} />\n </ReactLeptosContext.Provider>\n );\n};\n\nexport function useLeptosTunnel() {\n const [tunnel, setTunnel] = useState<Tunnel | undefined>(undefined);\n\n const addTunnel = (\n element: Element,\n node: ReactNode,\n context: LeptosContext,\n ) => {\n const id = Math.random().toString(36).slice(2);\n setTunnel({ element, node, id, context });\n return id; // Return id for later removal\n };\n\n const removeTunnel = () => {\n setTunnel(undefined);\n };\n\n const TunnelRenderer = () =>\n tunnel ? (\n createPortal(\n <ReactLeptosContext.Provider value={tunnel.context}>\n {tunnel.node}\n </ReactLeptosContext.Provider>,\n tunnel.element,\n tunnel.id,\n )\n ) : (\n <></>\n );\n\n useEffect(() => {\n return () => {\n if (tunnel) {\n //try{tunnel.context.cleanup();} catch (e){console.log(\"Error cleaning up leptos context:\",e)}\n }\n };\n });\n\n return {\n addTunnel,\n removeTunnel,\n TunnelRenderer,\n };\n}\n\nexport function useLeptosTunnels() {\n const [tunnels, setTunnels] = useState<Tunnel[]>([]);\n\n const addTunnel = (\n element: Element,\n node: ReactNode,\n context: LeptosContext,\n ) => {\n const id = Math.random().toString(36).slice(2);\n setTunnels((prev) => [...prev, { element, node, id, context }]);\n return id; // Return id for later removal\n };\n\n const removeTunnel = (id: string) => {\n setTunnels((prev) =>\n prev.filter((tunnel) => {\n if (tunnel.id === id) {\n //try{tunnel.context.cleanup();} catch (e){console.log(\"Error cleaning up leptos context:\",e)}\n }\n return tunnel.id !== id;\n }),\n );\n };\n\n const TunnelRenderer = () => (\n <>\n {tunnels.map((tunnel) =>\n createPortal(\n <ReactLeptosContext.Provider value={tunnel.context}>\n {tunnel.node}\n </ReactLeptosContext.Provider>,\n tunnel.element,\n tunnel.id,\n ),\n )}\n </>\n );\n\n useEffect(() => {\n return () => {\n tunnels.forEach((tunnel) => {\n //try{tunnel.context.cleanup();} catch (e){console.log(\"Error cleaning up leptos context:\",e)}\n });\n };\n });\n\n return {\n addTunnel,\n removeTunnel,\n TunnelRenderer,\n };\n}\n"],"mappings":"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","names":["FTML","useEffect","useRef","Fragment","jsx","initialize","useRef","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexiformal/ftml-react",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/FlexiFormal/ftml-ts#readme",
6
6
  "bugs": {
@@ -50,7 +50,7 @@
50
50
  "vitest": "^3.0.7"
51
51
  },
52
52
  "dependencies": {
53
- "@flexiformal/ftml": "=0.0.5",
53
+ "@flexiformal/ftml": "=0.0.7",
54
54
  "react": "^19.0.0",
55
55
  "react-dom": "^19.0.0"
56
56
  }
package/src/index.tsx CHANGED
@@ -151,12 +151,13 @@ export interface FTMLFragmentArgs extends FtmlConfig {
151
151
  * render an FTML fragment
152
152
  */
153
153
  export const FTMLFragment: React.FC<FTMLFragmentArgs> = (args) => {
154
- const mountRef = useRef<HTMLDivElement>(null);
154
+ const mountRef = useRef<HTMLDivElement | null>(null);
155
155
  const { addTunnel, TunnelRenderer } = useLeptosTunnels();
156
156
  const context = useContext(ReactLeptosContext);
157
157
 
158
158
  useEffect(() => {
159
159
  if (!mountRef.current) return;
160
+ //console.log("Mounting fragment ",args.fragment.uri);
160
161
  const handle = Base.ftmlFragment(
161
162
  mountRef.current,
162
163
  args.fragment,
@@ -164,6 +165,7 @@ export const FTMLFragment: React.FC<FTMLFragmentArgs> = (args) => {
164
165
  toConfig(args, addTunnel),
165
166
  );
166
167
  return () => {
168
+ //console.log("Unmounting fragment ",args.fragment.uri);
167
169
  handle.unmount();
168
170
  };
169
171
  }, []);