2
2
import { PluginReactContext } from 'Molstar/mol-plugin-ui/base' ;
3
3
import { PluginUIContext } from 'Molstar/mol-plugin-ui/context' ;
4
4
import { PluginUISpec } from 'Molstar/mol-plugin-ui/spec' ;
5
- import { ComponentProps , JSXElementConstructor , createElement } from 'react' ;
5
+ import { ComponentProps , JSXElementConstructor , createElement , useEffect , useState } from 'react' ;
6
6
import { createRoot } from 'react-dom/client' ;
7
7
import { DefaultPluginUISpec } from '../../spec' ;
8
8
@@ -36,9 +36,6 @@ export async function createPluginSplitUI(options: {
36
36
if ( onBeforeUIRender ) {
37
37
await onBeforeUIRender ( ctx ) ;
38
38
}
39
- if ( ! ctx . isInitialized ) {
40
- throw new Error ( 'NotImplementedError: React-rendering before PluginContext is initialized' ) ; // TODO implement a la src/mol-plugin-ui/plugin.tsx
41
- }
42
39
for ( const { target : element , component, props } of layout ) {
43
40
render ( < PluginPanelWrapper plugin = { ctx } component = { component } props = { props ?? { } } /> , resolveHTMLElement ( element ) ) ;
44
41
// TODO in future: consider adding a listener that re-renders the React component when the div is removed and re-added to DOM
@@ -61,8 +58,28 @@ export function resolveHTMLElement(element: HTMLElement | string): HTMLElement {
61
58
}
62
59
}
63
60
61
+ type LoadState = { kind : 'initialized' } | { kind : 'pending' } | { kind : 'error' , message : string }
64
62
65
63
function PluginPanelWrapper < P extends { } > ( { plugin, component, props } : { plugin : PluginUIContext , component : JSXElementConstructor < P > , props : P } ) {
64
+ const [ state , setState ] = useState < LoadState > ( { kind : 'pending' } ) ;
65
+ useEffect ( ( ) => {
66
+ setState ( plugin . isInitialized ? { kind : 'initialized' } : { kind : 'pending' } ) ;
67
+ let mounted = true ;
68
+ plugin . initialized . then ( ( ) => {
69
+ if ( mounted ) setState ( { kind : 'initialized' } ) ;
70
+ } ) . catch ( err => {
71
+ if ( mounted ) setState ( { kind : 'error' , message : `${ err } ` } ) ;
72
+ } ) ;
73
+ return ( ) => { mounted = false ; } ;
74
+ } , [ plugin ] ) ;
75
+
76
+ if ( state . kind !== 'initialized' ) {
77
+ const message = state . kind === 'error' ? `Initialization error: ${ state . message } ` : 'Waiting for plugin initialization' ;
78
+ return < div className = 'msp-plugin' style = { { position : 'relative' , width : '100%' , height : '100%' } } >
79
+ < div className = 'msp-plugin-init-error' > { message } </ div >
80
+ </ div > ;
81
+ }
82
+
66
83
return < PluginReactContext . Provider value = { plugin } >
67
84
< div className = 'msp-plugin' style = { { position : 'relative' , width : '100%' , height : '100%' } } >
68
85
< div className = 'msp-plugin-content msp-layout-standard' style = { { position : 'relative' , width : '100%' , height : '100%' } } >
0 commit comments