Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Skaiir committed Mar 20, 2024
1 parent 08cc3e7 commit 166f7fc
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FeelEntry, isFeelEntryEdited, TextAreaEntry, isTextAreaEntryEdited } from '@bpmn-io/properties-panel';
import { FeelEntry, isFeelEntryEdited, TextAreaEntry, isTextAreaEntryEdited, ToggleSwitchEntry, isToggleSwitchEntryEdited } from '@bpmn-io/properties-panel';
import { get } from 'min-dash';

import { useService, useVariables } from '../hooks';
Expand All @@ -12,7 +12,7 @@ export function JSFunctionEntry(props) {
const entries = [
{
id: 'variable-mappings',
component: VariableMappings,
component: FunctionParameters,
editField: editField,
field: field,
isEdited: isFeelEntryEdited,
Expand All @@ -25,13 +25,21 @@ export function JSFunctionEntry(props) {
field: field,
isEdited: isTextAreaEntryEdited,
isDefaultVisible: (field) => field.type === 'jsfunc'
},
{
id: 'on-load-only',
component: OnLoadOnlyEntry,
editField: editField,
field: field,
isEdited: isToggleSwitchEntryEdited,
isDefaultVisible: (field) => field.type === 'jsfunc'
}
];

return entries;
}

function VariableMappings(props) {
function FunctionParameters(props) {
const {
editField,
field,
Expand All @@ -42,7 +50,7 @@ function VariableMappings(props) {

const variables = useVariables().map(name => ({ name }));

const path = [ 'variableMappings' ];
const path = [ 'functionParameters' ];

const getValue = () => {
return get(field, path, '');
Expand All @@ -52,13 +60,23 @@ function VariableMappings(props) {
return editField(field, path, value || '');
};

const tooltip = <div>
Functions parameters should be described as an object, e.g.:
<pre><code>{`{
name: user.name,
age: user.age
}`}</code></pre>
</div>;

return FeelEntry({
debounce,
feel: 'required',
element: field,
getValue,
id,
label: 'Variable mappings',
label: 'Function parameters',
tooltip,
description: 'Define the parameters to pass to the javascript context.',
setValue,
variables
});
Expand Down Expand Up @@ -87,8 +105,35 @@ function FunctionDefinition(props) {
debounce,
element: field,
getValue,
description: 'Access function parameters via `data`, set results with `setValue`, and register cleanup functions with `onCleanup`.',
id,
label: 'Javascript code',
setValue
});
}

function OnLoadOnlyEntry(props) {
const {
editField,
field,
id
} = props;

const path = [ 'onLoadOnly' ];

const getValue = () => {
return !!get(field, path, false);
};

const setValue = (value) => {
editField(field, path, value);
};

return ToggleSwitchEntry({
element: field,
id,
label: 'Function',
label: 'Execute on load only',
getValue,
setValue
});
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { useCallback, useEffect } from 'preact/hooks';
import { useExpressionEvaluation, useDeepCompareMemoize } from '../../hooks';
import { useCallback, useEffect, useState } from 'preact/hooks';
import { useExpressionEvaluation, useDeepCompareMemoize, usePrevious } from '../../hooks';
import { isObject } from 'min-dash';

const type = 'jsfunc';

export function JSFunctionField(props) {
const { field, onChange } = props;
const { jsFunction, variableMappings } = field;
const { jsFunction, functionParameters, onLoadOnly } = field;

const data = useExpressionEvaluation(variableMappings);
const dataMemo = useDeepCompareMemoize(data);
const [ loadLatch, setLoadLatch ] = useState(false);

const evaluateExpression = useCallback(() => {
try {
const paramsEval = useExpressionEvaluation(functionParameters);
const params = useDeepCompareMemoize(isObject(paramsEval) ? paramsEval : {});

// return early if no dependency data
if (!isObject(dataMemo) || !Object.keys(dataMemo).length) {
return;
}
const functionMemo = useCallback((params) => {

const cleanupCallbacks = [];

const func = new Function('data', 'setResult', jsFunction);
func(dataMemo, value => onChange({ field, value }));
try {

setLoadLatch(true);
const func = new Function('data', 'setValue', 'onCleanup', jsFunction);
func(params, value => onChange({ field, value }), callback => cleanupCallbacks.push(callback));

} catch (error) {

Expand All @@ -32,11 +33,34 @@ export function JSFunctionField(props) {
console.error('Error evaluating expression:', error);
onChange({ field, value: null });
}
}, [ jsFunction, dataMemo, field, onChange ]);

return () => {
cleanupCallbacks.forEach(fn => fn());
};

}, [ jsFunction, field, onChange ]);

const previousFunctionMemo = usePrevious(functionMemo);
const previousParams = usePrevious(params);

useEffect(() => {
evaluateExpression();
}, [ evaluateExpression ]);

// reset load latch
if (!onLoadOnly && loadLatch) {
setLoadLatch(false);
}

const functionChanged = previousFunctionMemo !== functionMemo;
const paramsChanged = previousParams !== params;
const alreadyLoaded = onLoadOnly && loadLatch;

const shouldExecute = functionChanged || paramsChanged && !alreadyLoaded;

if (shouldExecute) {
return functionMemo(params);
}

}, [ previousFunctionMemo, functionMemo, previousParams, params, loadLatch, onLoadOnly ]);

return null;
}
Expand All @@ -48,6 +72,8 @@ JSFunctionField.config = {
keyed: true,
escapeGridRender: true,
create: (options = {}) => ({
jsFunction: 'setValue(data.value)',
functionParameters: '={\n value: 42\n}',
...options,
})
};

0 comments on commit 166f7fc

Please sign in to comment.