@@ -27,6 +27,21 @@ const MARGIN_LEGEND = 50; // Height of the legend
2727// they are also here since in some cases the <script>
2828// tag in the HTML file is sometimes not executed in time
2929const UTIL_FUNCTIONS = `
30+ /* queue helpers: available early via injectedJavaScriptBeforeContentLoaded */
31+ window.__uplot_queue__ = window.__uplot_queue__ || [];
32+ window.__uplot_enqueue__ = function(fn) {
33+ window.__uplot_queue__ = window.__uplot_queue__ || [];
34+ window.__uplot_queue__.push(fn);
35+ };
36+ window.__uplot_flush__ = function() {
37+ if (!window.__uplot_queue__) return;
38+ var q = window.__uplot_queue__;
39+ window.__uplot_queue__ = [];
40+ for (var i = 0; i < q.length; i++) {
41+ try { q[i](); } catch (e) { console.error('uplot queue fn error', e); }
42+ }
43+ };
44+
3045function parseOptions(options) {
3146 var parsed = JSON.parse(options, (k, v) => {
3247 if (typeof v === 'string' && v.startsWith('__UPLOT_FUNC__')) {
@@ -201,28 +216,84 @@ function getCreateChartString(
201216 return `
202217 (function() {
203218 ${ chartCreatedCheck }
204- window.__CHART_CREATED__ = true;
205219
206- // inject custom functions if provided
220+ // ensure helper functions are available
207221 ${ injectedJavaScript }
208222
209223 document.body.style.backgroundColor='${ bgColor } ';
210224
211225 // stash your data on window if provided
212226 ${ dataAssignment }
213227
214- // stash options on window
215- window._opts = parseOptions('${ stringify ( options ) } ');
228+ // helper that actually builds the chart (keeps errors visible)
229+ function __createUPlotChart() {
230+ try {
231+ window._opts = parseOptions('${ stringify ( options ) } ');
216232
217- if (window._chart) window._chart.destroy();
233+ if (window._chart) {
234+ try { window._chart.destroy(); } catch (e) {}
235+ }
218236
219- // now actually construct uPlot
220- window._chart = new uPlot(window._opts, window._data, document.getElementById('chart'));
237+ window._chart = new uPlot(window._opts, window._data, document.getElementById('chart'));
238+
239+ // mark created and flush queued commands
240+ window.__CHART_CREATED__ = true;
241+ if (window.__uplot_flush__) {
242+ try { window.__uplot_flush__(); } catch (e) { console.error('flush error', e); }
243+ }
244+
245+ console.log('uPlot chart created');
246+ } catch (err) {
247+ console.error('createUPlotChart error', err && err.message ? err.message : err);
248+ window.__CHART_CREATED__ = false;
249+ }
250+ }
251+
252+ // If uPlot is already loaded, create immediately; otherwise poll until available (timeout)
253+ if (typeof window.uPlot !== 'undefined') {
254+ __createUPlotChart();
255+ } else {
256+ var __waitCount = 0;
257+ var __waitMax = 20; // ~1s with 50ms interval
258+ var __iv = setInterval(function() {
259+ if (typeof window.uPlot !== 'undefined') {
260+ clearInterval(__iv);
261+ __createUPlotChart();
262+ return;
263+ }
264+ __waitCount++;
265+ if (__waitCount >= __waitMax) {
266+ clearInterval(__iv);
267+ console.error('uPlot not found after timeout; ensure uPlot script is included in the HTML');
268+ }
269+ }, 50);
270+ }
221271 })();
222272 true;
223273 ` ;
224274}
225275
276+ // helper used on the RN side to wrap injected snippets so they either run now or enqueue
277+ const runWhenReady = ( body : string ) : string => {
278+ return `
279+ (function(){
280+ function __run(){ ${ body } }
281+ if (typeof window !== 'undefined' && window._chart) {
282+ __run();
283+ } else {
284+ // prefer enqueue helper if present
285+ if (window.__uplot_enqueue__) {
286+ window.__uplot_enqueue__(__run);
287+ } else {
288+ window.__uplot_queue__ = window.__uplot_queue__ || [];
289+ window.__uplot_queue__.push(__run);
290+ }
291+ }
292+ })();
293+ true;
294+ ` ;
295+ } ;
296+
226297export interface UPlotProps {
227298 /** uPlot data array: [xValues[], series1[], series2[], ...] */
228299 data : [ number [ ] , ...number [ ] [ ] ] ;
@@ -396,25 +467,6 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
396467 }
397468 } , [ data ] ) ;
398469
399- // var { optionsFinal, containerWidth, containerHeight } = useMemo(() => {
400-
401- // const { containerWidth, containerHeight } = dimensionsRef.current.containerWidth
402- // ? dimensionsRef.current
403- // : { containerWidth: width, containerHeight: height };
404-
405- // return getDimensions(options, style, containerWidth, containerHeight, margin);
406- // }, [style, width, height]);
407-
408- // var optsFinal = useMemo(() => {
409- // return options
410- // // return getDimensions(
411- // // options,
412- // // dimensionsRef.current.containerWidth,
413- // // dimensionsRef.current.containerHeight,
414- // // margin,
415- // // );
416- // }, [style, margin]);
417-
418470 useEffect ( ( ) => {
419471 // update uplot height and width if options change
420472
@@ -429,14 +481,14 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
429481 return ;
430482 }
431483
432- webref . current . injectJavaScript ( `
484+ const body = `
433485 if (window._chart) {
434486 window._chart.setSize(${ JSON . stringify ( dimensionsRef . current . containerWidth ) } , ${ JSON . stringify ( dimensionsRef . current . containerHeight ) } );
435487 } else {
436488 console.error('useEffect - dim | Chart not initialized');
437489 }
438- true ;
439- ` ) ;
490+ ` ;
491+ webref . current . injectJavaScript ( runWhenReady ( body ) ) ;
440492 }
441493 } , [
442494 dimensionsRef . current . containerHeight ,
@@ -475,9 +527,16 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
475527
476528 // Ensure dataRef is the source of truth for the created chart in the WebView
477529 dataRef . current = toPlainArrays ( chartData || [ ] ) as number [ ] [ ] ;
478- webref . current . injectJavaScript (
479- getCreateChartString ( dataRef . current , optsFinal , bgColor ) ,
530+ const createChartStr = getCreateChartString (
531+ dataRef . current ,
532+ optsFinal ,
533+ bgColor ,
534+ UTIL_FUNCTIONS ,
480535 ) ;
536+
537+ console . log ( 'createChartStr' , createChartStr ) ;
538+
539+ webref . current . injectJavaScript ( createChartStr ) ;
481540 }
482541 initialized . current = true ;
483542 } ,
@@ -491,8 +550,6 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
491550 * @param {Object } newOptions - The new options to set for the chart.
492551 */
493552 const updateOptions = useCallback ( ( newOptions : any ) : void => {
494- // call getDimensions to update optionsFinal
495-
496553 destroy ( true ) ; // keep data
497554 createChart ( newOptions ) ;
498555 } , [ ] ) ;
@@ -518,16 +575,15 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
518575 return ;
519576 }
520577
521- // Mirror full data into window._data and set chart
522- webref . current . injectJavaScript ( `
578+ const body = `
523579 window._data = ${ JSON . stringify ( dataRef . current ) } ;
524580 if (window._chart) {
525581 window._chart.setData(window._data);
526582 } else {
527583 console.error('setData | Chart not initialized');
528584 }
529- true ;
530- ` ) ;
585+ ` ;
586+ webref . current . injectJavaScript ( runWhenReady ( body ) ) ;
531587 } , [ ] ) ;
532588
533589 /**
@@ -553,22 +609,20 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
553609
554610 // For native: inject only the new item and append to window._data in the WebView,
555611 // then call setData there. Avoid sending the entire dataRef.
556- webref . current . injectJavaScript ( `
557- (function() {
558- var item = ${ JSON . stringify ( item ) } ;
559- if (!window._data || window._data.length !== item.length) {
560- window._data = [];
561- for (var i = 0; i < item.length; i++) window._data.push([]);
562- }
563- for (var j = 0; j < item.length; j++) window._data[j].push(item[j]);
564- if (window._chart) {
565- window._chart.setData(window._data);
566- } else {
567- console.error('pushData | Chart not initialized');
568- }
569- })();
570- true;
571- ` ) ;
612+ const body = `
613+ var item = ${ JSON . stringify ( item ) } ;
614+ if (!window._data || window._data.length !== item.length) {
615+ window._data = [];
616+ for (var i = 0; i < item.length; i++) window._data.push([]);
617+ }
618+ for (var j = 0; j < item.length; j++) window._data[j].push(item[j]);
619+ if (window._chart) {
620+ window._chart.setData(window._data);
621+ } else {
622+ console.error('pushData | Chart not initialized');
623+ }
624+ ` ;
625+ webref . current . injectJavaScript ( runWhenReady ( body ) ) ;
572626 } , [ ] ) ;
573627
574628 /**
@@ -601,16 +655,15 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
601655 }
602656
603657 // Mirror sliced data into window._data and update the chart in the WebView
604- webref . current . injectJavaScript ( `
658+ const body = `
605659 window._data = ${ JSON . stringify ( dataRef . current ) } ;
606660 if (window._chart) {
607661 window._chart.setData(window._data);
608662 } else {
609663 console.error('sliceSeries | Chart not initialized or data not available');
610664 }
611- true;
612- ` ) ;
613- return ;
665+ ` ;
666+ webref . current . injectJavaScript ( runWhenReady ( body ) ) ;
614667 } ,
615668 [ ] ,
616669 ) ;
@@ -625,14 +678,14 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
625678 return ;
626679 }
627680
628- webref . current . injectJavaScript ( `
681+ const body = `
629682 if (window._chart) {
630- window._chart.setScale(${ JSON . stringify ( axis ) } , ${ JSON . stringify ( options ) } );true;
683+ window._chart.setScale(${ JSON . stringify ( axis ) } , ${ JSON . stringify ( options ) } );
631684 } else {
632685 console.error('setScale | Chart not initialized');
633686 }
634- true ;
635- ` ) ;
687+ ` ;
688+ webref . current . injectJavaScript ( runWhenReady ( body ) ) ;
636689 }
637690 } , [ ] ) ;
638691
@@ -671,14 +724,14 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
671724 return ;
672725 }
673726
674- webref . current . injectJavaScript ( `
675- if (! window._chart) {
676- window._chart.setSize(${ JSON . stringify ( width ) } , ${ JSON . stringify ( height ) } );true;
727+ const body = `
728+ if (window._chart) {
729+ window._chart.setSize(${ JSON . stringify ( width ) } , ${ JSON . stringify ( height ) } );
677730 } else {
678731 console.error('setSize | Chart not initialized');
679732 }
680- true ;
681- ` ) ;
733+ ` ;
734+ webref . current . injectJavaScript ( runWhenReady ( body ) ) ;
682735 }
683736 } , [ ] ) ;
684737
@@ -697,15 +750,17 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
697750
698751 var keepDataStr = keepData ? '' : `window._data = [];` ;
699752
700- webref . current . injectJavaScript ( `
753+ const body = `
701754 ${ keepDataStr }
702-
703755 if (window._chart) {
704- window._chart.destroy();
756+ try { window._chart.destroy(); } catch (e) {}
705757 }
706758 window.__CHART_CREATED__ = false;
707- true;
708- ` ) ;
759+ // clear queued ops to avoid stale calls after destroy
760+ window.__uplot_queue__ = [];
761+ ` ;
762+ // run immediately (no need to queue)
763+ webref . current . injectJavaScript ( `${ body } true;` ) ;
709764 }
710765 initialized . current = false ;
711766 } , [ ] ) ;
@@ -730,11 +785,6 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
730785 ` ;
731786 } , [ injectedJavaScript ] ) ;
732787
733- console . log (
734- 'injectedJavaScriptWithFunctions' ,
735- injectedJavaScriptWithFunctions ,
736- ) ;
737-
738788 useImperativeHandle ( ref , ( ) => ( {
739789 createChart,
740790 updateOptions,
@@ -768,7 +818,9 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
768818 ref = { setWebRef }
769819 onLayout = { handleLayout }
770820 javaScriptEnabled = { true }
771- injectedJavaScript = { injectedJavaScriptWithFunctions }
821+ injectedJavaScriptBeforeContentLoaded = {
822+ injectedJavaScriptWithFunctions
823+ }
772824 onMessage = { handleMessage }
773825 />
774826 ) ;
0 commit comments