mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-13 07:42:55 +01:00
feat(blockly): Phase A foundation - Blockly setup, custom blocks, and generators
- Install blockly package (~500KB) - Create BlocklyWorkspace React component with serialization - Define custom Noodl blocks (Input/Output, Variables, Objects, Arrays) - Implement JavaScript code generators for all custom blocks - Add theme-aware styling for Blockly workspace - Export initialization functions for easy integration Part of TASK-012: Blockly Visual Logic Integration
This commit is contained in:
@@ -37,6 +37,84 @@ const ExpressionNode = {
|
||||
this._internal.unsubscribe();
|
||||
this._internal.unsubscribe = null;
|
||||
}
|
||||
},
|
||||
registerInputIfNeeded: function (name) {
|
||||
if (this.hasInput(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._internal.scope[name] = 0;
|
||||
this._inputValues[name] = 0;
|
||||
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
this._internal.scope[name] = value;
|
||||
if (!this.isInputConnected('run')) this._scheduleEvaluateExpression();
|
||||
}
|
||||
});
|
||||
},
|
||||
_scheduleEvaluateExpression: function () {
|
||||
var internal = this._internal;
|
||||
if (internal.hasScheduledEvaluation === false) {
|
||||
internal.hasScheduledEvaluation = true;
|
||||
this.flagDirty();
|
||||
this.scheduleAfterInputsHaveUpdated(function () {
|
||||
var lastValue = internal.cachedValue;
|
||||
internal.cachedValue = this._calculateExpression();
|
||||
if (lastValue !== internal.cachedValue) {
|
||||
this.flagOutputDirty('result');
|
||||
this.flagOutputDirty('isTrue');
|
||||
this.flagOutputDirty('isFalse');
|
||||
}
|
||||
if (internal.cachedValue) this.sendSignalOnOutput('isTrueEv');
|
||||
else this.sendSignalOnOutput('isFalseEv');
|
||||
internal.hasScheduledEvaluation = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
_calculateExpression: function () {
|
||||
var internal = this._internal;
|
||||
|
||||
if (!internal.compiledFunction) {
|
||||
internal.compiledFunction = this._compileFunction();
|
||||
}
|
||||
|
||||
for (var i = 0; i < internal.inputNames.length; ++i) {
|
||||
var inputValue = internal.scope[internal.inputNames[i]];
|
||||
internal.inputValues[i] = inputValue;
|
||||
}
|
||||
|
||||
// Get proper Noodl API and append as last parameter for backward compatibility
|
||||
const JavascriptNodeParser = require('../../javascriptnodeparser');
|
||||
const noodlAPI = JavascriptNodeParser.createNoodlAPI(this.context && this.context.modelScope);
|
||||
const argsWithNoodl = internal.inputValues.concat([noodlAPI]);
|
||||
|
||||
try {
|
||||
return internal.compiledFunction.apply(null, argsWithNoodl);
|
||||
} catch (e) {
|
||||
console.error('Error in expression:', e.message);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
_compileFunction: function () {
|
||||
var expression = this._internal.currentExpression;
|
||||
var args = Object.keys(this._internal.scope);
|
||||
|
||||
// Add 'Noodl' as last parameter for backward compatibility
|
||||
args.push('Noodl');
|
||||
|
||||
var key = expression + args.join(' ');
|
||||
|
||||
if (compiledFunctionsCache.hasOwnProperty(key) === false) {
|
||||
args.push(expression);
|
||||
|
||||
try {
|
||||
compiledFunctionsCache[key] = construct(Function, args);
|
||||
} catch (e) {
|
||||
console.error('Failed to compile JS function', e.message);
|
||||
}
|
||||
}
|
||||
return compiledFunctionsCache[key];
|
||||
}
|
||||
},
|
||||
getInspectInfo() {
|
||||
@@ -197,84 +275,6 @@ const ExpressionNode = {
|
||||
return !!this._internal.cachedValue;
|
||||
}
|
||||
}
|
||||
},
|
||||
prototypeExtensions: {
|
||||
registerInputIfNeeded: {
|
||||
value: function (name) {
|
||||
if (this.hasInput(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._internal.scope[name] = 0;
|
||||
this._inputValues[name] = 0;
|
||||
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
this._internal.scope[name] = value;
|
||||
if (!this.isInputConnected('run')) this._scheduleEvaluateExpression();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
_scheduleEvaluateExpression: {
|
||||
value: function () {
|
||||
var internal = this._internal;
|
||||
if (internal.hasScheduledEvaluation === false) {
|
||||
internal.hasScheduledEvaluation = true;
|
||||
this.flagDirty();
|
||||
this.scheduleAfterInputsHaveUpdated(function () {
|
||||
var lastValue = internal.cachedValue;
|
||||
internal.cachedValue = this._calculateExpression();
|
||||
if (lastValue !== internal.cachedValue) {
|
||||
this.flagOutputDirty('result');
|
||||
this.flagOutputDirty('isTrue');
|
||||
this.flagOutputDirty('isFalse');
|
||||
}
|
||||
if (internal.cachedValue) this.sendSignalOnOutput('isTrueEv');
|
||||
else this.sendSignalOnOutput('isFalseEv');
|
||||
internal.hasScheduledEvaluation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
_calculateExpression: {
|
||||
value: function () {
|
||||
var internal = this._internal;
|
||||
|
||||
if (!internal.compiledFunction) {
|
||||
internal.compiledFunction = this._compileFunction();
|
||||
}
|
||||
for (var i = 0; i < internal.inputNames.length; ++i) {
|
||||
var inputValue = internal.scope[internal.inputNames[i]];
|
||||
internal.inputValues[i] = inputValue;
|
||||
}
|
||||
try {
|
||||
return internal.compiledFunction.apply(null, internal.inputValues);
|
||||
} catch (e) {
|
||||
console.error('Error in expression:', e.message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
_compileFunction: {
|
||||
value: function () {
|
||||
var expression = this._internal.currentExpression;
|
||||
var args = Object.keys(this._internal.scope);
|
||||
|
||||
var key = expression + args.join(' ');
|
||||
|
||||
if (compiledFunctionsCache.hasOwnProperty(key) === false) {
|
||||
args.push(expression);
|
||||
|
||||
try {
|
||||
compiledFunctionsCache[key] = construct(Function, args);
|
||||
} catch (e) {
|
||||
console.error('Failed to compile JS function', e.message);
|
||||
}
|
||||
}
|
||||
return compiledFunctionsCache[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -132,11 +132,18 @@ const SimpleJavascriptNode = {
|
||||
}
|
||||
}
|
||||
|
||||
// Create Noodl API and augment with Inputs/Outputs for backward compatibility
|
||||
// Legacy code used: Noodl.Outputs.foo = 'bar'
|
||||
// New code uses: Outputs.foo = 'bar' (direct parameter)
|
||||
const noodlAPI = JavascriptNodeParser.createNoodlAPI(this.nodeScope.modelScope);
|
||||
noodlAPI.Inputs = inputs;
|
||||
noodlAPI.Outputs = outputs;
|
||||
|
||||
try {
|
||||
await func.apply(this._internal._this, [
|
||||
inputs,
|
||||
outputs,
|
||||
JavascriptNodeParser.createNoodlAPI(this.nodeScope.modelScope),
|
||||
noodlAPI,
|
||||
JavascriptNodeParser.getComponentScopeForNode(this)
|
||||
]);
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user