|
| 1 | +import Sandbox from 'websandbox'; |
1 | 2 | import { useCallback, useEffect, useState } from 'preact/hooks'; |
2 | | -import { useExpressionEvaluation, useDeepCompareMemoize, usePrevious } from '../../hooks'; |
| 3 | +import { useExpressionEvaluation, useDeepCompareMemoize } from '../../hooks'; |
3 | 4 | import { isObject } from 'min-dash'; |
4 | 5 |
|
5 | | -const type = 'script'; |
6 | 6 |
|
7 | 7 | export function JSFunctionField(props) { |
8 | 8 | const { field, onChange } = props; |
9 | | - const { jsFunction, functionParameters, onLoadOnly } = field; |
| 9 | + const { jsFunction, functionParameters } = field; |
10 | 10 |
|
11 | | - const [ loadLatch, setLoadLatch ] = useState(false); |
| 11 | + const [ sandbox, setSandbox ] = useState(null); |
12 | 12 |
|
13 | 13 | const paramsEval = useExpressionEvaluation(functionParameters); |
14 | 14 | const params = useDeepCompareMemoize(isObject(paramsEval) ? paramsEval : {}); |
15 | 15 |
|
16 | | - const functionMemo = useCallback((params) => { |
17 | | - |
18 | | - const cleanupCallbacks = []; |
19 | | - |
20 | | - try { |
21 | | - |
22 | | - setLoadLatch(true); |
23 | | - const func = new Function('data', 'setValue', 'onCleanup', jsFunction); |
24 | | - func(params, value => onChange({ field, value }), callback => cleanupCallbacks.push(callback)); |
25 | | - |
26 | | - } catch (error) { |
27 | | - |
28 | | - // invalid expression definition, may happen during editing |
29 | | - if (error instanceof SyntaxError) { |
30 | | - return; |
| 16 | + const rebuildSandbox = useCallback(() => { |
| 17 | + const localApi = { |
| 18 | + setValue: function(value) { |
| 19 | + onChange({ field, value }); |
31 | 20 | } |
32 | | - |
33 | | - console.error('Error evaluating expression:', error); |
34 | | - onChange({ field, value: null }); |
35 | | - } |
36 | | - |
37 | | - return () => { |
38 | | - cleanupCallbacks.forEach(fn => fn()); |
39 | 21 | }; |
40 | 22 |
|
41 | | - }, [ jsFunction, field, onChange ]); |
42 | | - |
43 | | - const previousFunctionMemo = usePrevious(functionMemo); |
44 | | - const previousParams = usePrevious(params); |
| 23 | + const newSandbox = Sandbox.create(localApi, { |
| 24 | + frameContainer: '.iframe__container', |
| 25 | + frameClassName: 'simple__iframe' |
| 26 | + }); |
45 | 27 |
|
46 | | - useEffect(() => { |
| 28 | + newSandbox.promise.then((sandboxInstance) => { |
| 29 | + setSandbox(sandboxInstance); |
| 30 | + sandboxInstance.run(` |
| 31 | + Websandbox.connection.setLocalApi({ |
| 32 | + onInit: () => Websandbox.connection.remote.onInit(), |
| 33 | + onData: (data) => Websandbox.connection.remote.onData(data), |
| 34 | + }); |
47 | 35 |
|
48 | | - // reset load latch |
49 | | - if (!onLoadOnly && loadLatch) { |
50 | | - setLoadLatch(false); |
51 | | - } |
| 36 | + // Custom user code |
| 37 | + ${jsFunction} |
| 38 | + `); |
52 | 39 |
|
53 | | - const functionChanged = previousFunctionMemo !== functionMemo; |
54 | | - const paramsChanged = previousParams !== params; |
55 | | - const alreadyLoaded = onLoadOnly && loadLatch; |
| 40 | + sandboxInstance.connection.remote.onInit(); |
| 41 | + }); |
| 42 | + }, [ jsFunction, onChange, field ]); |
56 | 43 |
|
57 | | - const shouldExecute = functionChanged || paramsChanged && !alreadyLoaded; |
| 44 | + useEffect(() => { |
| 45 | + rebuildSandbox(); |
| 46 | + }, [ rebuildSandbox ]); |
58 | 47 |
|
59 | | - if (shouldExecute) { |
60 | | - return functionMemo(params); |
| 48 | + useEffect(() => { |
| 49 | + if (sandbox && sandbox.connection && sandbox.connection.remote.onData) { |
| 50 | + sandbox.connection.remote.onData(params); |
61 | 51 | } |
62 | | - |
63 | | - }, [ previousFunctionMemo, functionMemo, previousParams, params, loadLatch, onLoadOnly ]); |
| 52 | + }, [ params, sandbox ]); |
64 | 53 |
|
65 | 54 | return null; |
66 | 55 | } |
67 | 56 |
|
68 | 57 | JSFunctionField.config = { |
69 | | - type, |
| 58 | + type: 'script', |
70 | 59 | label: 'JS Function', |
71 | 60 | group: 'basic-input', |
72 | 61 | keyed: true, |
|
0 commit comments