@@ -8,6 +8,7 @@ import type {
88 ReactPyModule ,
99 BindImportSource ,
1010 ReactPyModuleBinding ,
11+ ImportSourceBinding ,
1112} from "./types" ;
1213import log from "./logger" ;
1314
@@ -75,13 +76,15 @@ function createImportSourceElement(props: {
7576 if (
7677 ! isImportSourceEqual ( props . currentImportSource , props . model . importSource )
7778 ) {
78- log . error (
79- "Parent element import source " +
80- stringifyImportSource ( props . currentImportSource ) +
81- " does not match child's import source " +
82- stringifyImportSource ( props . model . importSource ) ,
83- ) ;
84- return null ;
79+ return props . binding . create ( "reactpy-child" , {
80+ ref : ( node : ReactPyChild | null ) => {
81+ if ( node ) {
82+ node . client = props . client ;
83+ node . model = props . model ;
84+ node . requestUpdate ( ) ;
85+ }
86+ } ,
87+ } ) ;
8588 } else {
8689 type = getComponentFromModule (
8790 props . module ,
@@ -270,3 +273,85 @@ function generic_reactjs_bind(node: HTMLElement) {
270273 unmount : ( ) => preact . render ( null , node ) ,
271274 } ;
272275}
276+
277+ class ReactPyChild extends HTMLElement {
278+ mountPoint : HTMLDivElement ;
279+ binding : ImportSourceBinding | null = null ;
280+ _client : ReactPyClientInterface | null = null ;
281+ _model : ReactPyVdom | null = null ;
282+ currentImportSource : ReactPyVdomImportSource | null = null ;
283+
284+ constructor ( ) {
285+ super ( ) ;
286+ this . mountPoint = document . createElement ( "div" ) ;
287+ this . mountPoint . style . display = "contents" ;
288+ }
289+
290+ connectedCallback ( ) {
291+ this . appendChild ( this . mountPoint ) ;
292+ }
293+
294+ set client ( value : ReactPyClientInterface ) {
295+ this . _client = value ;
296+ }
297+
298+ set model ( value : ReactPyVdom ) {
299+ this . _model = value ;
300+ }
301+
302+ requestUpdate ( ) {
303+ this . update ( ) ;
304+ }
305+
306+ async update ( ) {
307+ if ( ! this . _client || ! this . _model || ! this . _model . importSource ) {
308+ return ;
309+ }
310+
311+ const newImportSource = this . _model . importSource ;
312+
313+ if (
314+ ! this . binding ||
315+ ! this . currentImportSource ||
316+ ! isImportSourceEqual ( this . currentImportSource , newImportSource )
317+ ) {
318+ if ( this . binding ) {
319+ this . binding . unmount ( ) ;
320+ this . binding = null ;
321+ }
322+
323+ this . currentImportSource = newImportSource ;
324+
325+ try {
326+ const bind = await loadImportSource ( newImportSource , this . _client ) ;
327+ if ( this . isConnected ) {
328+ this . binding = bind ( this . mountPoint ) ;
329+ if ( this . binding ) {
330+ this . binding . render ( this . _model ) ;
331+ }
332+ }
333+ } catch ( error ) {
334+ console . error ( "Failed to load import source" , error ) ;
335+ }
336+ } else {
337+ if ( this . binding ) {
338+ this . binding . render ( this . _model ) ;
339+ }
340+ }
341+ }
342+
343+ disconnectedCallback ( ) {
344+ if ( this . binding ) {
345+ this . binding . unmount ( ) ;
346+ this . binding = null ;
347+ this . currentImportSource = null ;
348+ }
349+ }
350+ }
351+
352+ if (
353+ typeof customElements !== "undefined" &&
354+ ! customElements . get ( "reactpy-child" )
355+ ) {
356+ customElements . define ( "reactpy-child" , ReactPyChild ) ;
357+ }
0 commit comments