Skip to content

Commit 701e482

Browse files
committed
change strategy for injecting functions
1 parent abbf10e commit 701e482

File tree

2 files changed

+60
-41
lines changed

2 files changed

+60
-41
lines changed

src/components/ChartUPlot.tsx

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ import React, {
99
import { Platform, useWindowDimensions, View } from 'react-native';
1010
import { WebView } from 'react-native-webview';
1111

12-
var isWeb = Platform.OS === 'web';
13-
1412
import html from './uplot.html';
15-
1613
import 'uplot/dist/uPlot.min.css';
14+
var isWeb = Platform.OS === 'web';
1715

1816
var uPlot: any = null;
1917
if (isWeb) {
@@ -72,19 +70,52 @@ function getDimensions(
7270
return { optionsFinal, containerWidth, containerHeight };
7371
}
7472

73+
function getCreateChartString(
74+
data: number[][],
75+
options: any,
76+
bgColor: string = 'transparent',
77+
injectedJavaScript: string = '',
78+
): string {
79+
return `
80+
(function() {
81+
if (window.__CHART_CREATED__) return;
82+
window.__CHART_CREATED__ = true;
83+
84+
// inject custom functions if provided
85+
${injectedJavaScript}
86+
87+
document.body.style.backgroundColor='${bgColor}';
88+
89+
// stash your data and options on window
90+
window._data = ${JSON.stringify(data)};
91+
window._opts = parseOptions('${stringify(options)}');
92+
93+
// inject background color before chart setup if provided
94+
if ('${bgColor}') {
95+
document.body.style.backgroundColor='${bgColor}';
96+
}
97+
// now actually construct uPlot
98+
window._chart = new uPlot(window._opts, window._data, document.getElementById('chart'));
99+
})();
100+
true;
101+
`;
102+
}
103+
75104
export interface UPlotProps {
76105
/** uPlot data array: [xValues[], series1[], series2[], ...] */
77106
data: [number[], ...number[][]];
78107
/** uPlot options object */
79108
options: any;
80109
/** Additional style for the container */
81110
style?: any;
82-
/** any custom functions to be injected into the webview */
83-
functions?: string[];
111+
/** any custom functions to be injected into the webview (Function or source string) */
112+
functions?: Array<Function | string>;
84113
/** Margin for the chart, useful for titles and legends */
85114
margin?: { title?: number; legend?: number };
86115
/** Callback for messages from the WebView */
87116
onMessage?: (event: any) => void;
117+
/** JavaScript to be injected into the WebView */
118+
injectedJavaScript?: string;
88119
/** Additional props for the WebView */
89120
webviewProps?: any;
90121
}
@@ -98,6 +129,7 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
98129
functions,
99130
margin = { title: MARGIN_TITLE, legend: MARGIN_LEGEND },
100131
onMessage,
132+
injectedJavaScript = '',
101133
...webviewProps
102134
},
103135
ref,
@@ -116,10 +148,11 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
116148
}, [options, style, width, height]);
117149

118150
var injectFns = useMemo(() => {
119-
if (!functions || functions.length === 0) {
120-
return '';
121-
}
122-
return functions.join('\n');
151+
if (!functions || functions.length === 0) return '';
152+
// convert functions to string if needed
153+
return functions
154+
.map((fn) => (typeof fn === 'function' ? fn.toString() : fn))
155+
.join('\n');
123156
}, [functions]);
124157

125158
useEffect(() => {
@@ -144,7 +177,7 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
144177

145178
// eslint-disable-next-line @typescript-eslint/no-explicit-any
146179
const createChart = useCallback(
147-
(opts: any, data: number[][]): void => {
180+
(opts: any, data: number[][], bgColor?: string): void => {
148181
if (initialized.current) {
149182
return;
150183
}
@@ -153,25 +186,12 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
153186
uplotInstance.current = new uPlot(opts, data, webref);
154187
} else {
155188
// inject background color before chart setup if provided
156-
const bgJS = bgColor
157-
? `document.body.style.backgroundColor='${bgColor}';`
158-
: '';
159-
webref?.injectJavaScript(
160-
`${bgJS}
161189

162-
console.debug('Creating uPlot chart...');
163-
164-
window._data = ${JSON.stringify(data)};
165-
window._opts = parseOptions('${stringify(opts)}');
166-
167-
window._chart = new uPlot(window._opts, window._data, document.getElementById("chart"));
168-
true;
169-
`,
170-
);
190+
webref?.injectJavaScript(getCreateChartString(data, opts, bgColor));
171191
}
172192
initialized.current = true;
173193
},
174-
[bgColor],
194+
[],
175195
);
176196

177197
const setData = useCallback((newData: number[][]): void => {
@@ -310,26 +330,13 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
310330
}}
311331
scrollEnabled={false}
312332
onLoadEnd={(): void => {
313-
// webref.injectJavaScript(guardAndCreateJS);
314-
createChart(optionsFinal, data);
333+
createChart(optionsFinal, data, bgColor);
315334
}}
316335
ref={(r) => {
317336
webref = r;
318337
}}
319338
javaScriptEnabled={true}
320-
injectedJavaScript={`
321-
const consoleLog = (type, log) => window.ReactNativeWebView.postMessage(JSON.stringify({'type': 'Console', 'data': {'type': type, 'log': log}}));
322-
console = {
323-
log: (log) => consoleLog('log', log),
324-
debug: (log) => consoleLog('debug', log),
325-
info: (log) => consoleLog('info', log),
326-
warn: (log) => consoleLog('warn', log),
327-
error: (log) => consoleLog('error', log),
328-
};
329-
330-
${injectFns}
331-
true;
332-
`}
339+
injectedJavaScript={`${injectedJavaScript}; true;`}
333340
onMessage={(payload): void => {
334341
// in webviewProps, if onMessage is provided, call it with the payload
335342
if (onMessage) {

src/components/uplot.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
<head>
33
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
44
<script>
5+
const consoleLog = (type, log) =>
6+
window.ReactNativeWebView.postMessage(
7+
JSON.stringify({ type: 'Console', data: { type: type, log: log } }),
8+
);
9+
console = {
10+
log: (log) => consoleLog('log', log),
11+
debug: (log) => consoleLog('debug', log),
12+
info: (log) => consoleLog('info', log),
13+
warn: (log) => consoleLog('warn', log),
14+
error: (log) => consoleLog('error', log),
15+
};
16+
517
function parseOptions(options) {
618
var parsed = JSON.parse(options, (k, v) => {
719
if (
@@ -12,7 +24,7 @@
1224
if (fnMatch) {
1325
return window[fnMatch[1]];
1426
} else {
15-
console.error(`Invalid function format: ${v}`);
27+
console.error('Invalid function format');
1628
return () => {};
1729
}
1830
}

0 commit comments

Comments
 (0)