blockly-fluid 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/index.js +192 -113
- package/index.ts +1 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Blocks["controls_repeat_forever"] = {
|
|
|
49
49
|
},
|
|
50
50
|
previous: true,
|
|
51
51
|
next: true,
|
|
52
|
-
tooltip: "Repeat the enclosed blocks forever"
|
|
52
|
+
tooltip: "Repeat the enclosed blocks forever"
|
|
53
53
|
};
|
|
54
54
|
```
|
|
55
55
|
|
|
@@ -103,4 +103,8 @@ Blocks["metadata_set_description"] = {
|
|
|
103
103
|
|
|
104
104
|
## License 📄
|
|
105
105
|
|
|
106
|
-
MIT License — free to use, modify, and distribute!
|
|
106
|
+
MIT License — free to use, modify, and distribute!
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
*Made with ❤️ (and probably caffeine) by [Dinoscape](https://github.com/DinoscapeProgramming)*
|
package/index.js
CHANGED
|
@@ -1,122 +1,201 @@
|
|
|
1
|
-
module.exports = (Blockly, { translate = (text) => text } = {}) =>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
1
|
+
module.exports = (Blockly, { generator: languageGeneratorFallback, generators: languageGenerators = {}, translate = (text) => text } = {}) => {
|
|
2
|
+
const Blocks = new Proxy({}, {
|
|
3
|
+
set(_, name, {
|
|
4
|
+
inputs = {},
|
|
5
|
+
layout = [],
|
|
6
|
+
fields = {},
|
|
7
|
+
statements = {},
|
|
8
|
+
previous = true,
|
|
9
|
+
next = true,
|
|
10
|
+
output,
|
|
11
|
+
color,
|
|
12
|
+
inline = true,
|
|
13
|
+
deletable = true,
|
|
14
|
+
tooltip = "",
|
|
15
|
+
help = "",
|
|
16
|
+
generator: blockGeneratorFallback,
|
|
17
|
+
generators: blockGenerators = {}
|
|
18
|
+
}) {
|
|
19
|
+
Blockly.Blocks[name] = {
|
|
20
|
+
init: function () {
|
|
21
|
+
if (typeof inputs === "function") (inputs = inputs());
|
|
22
|
+
if (typeof layout === "function") (layout = layout());
|
|
23
|
+
if (typeof statements === "function") (statements = statements());
|
|
24
|
+
|
|
25
|
+
if ((Array.isArray(layout)) ? layout.length : ((typeof layout === "string") ? layout : false)) {
|
|
26
|
+
((Array.isArray(layout)) ? layout : [layout]).forEach((token) => {
|
|
27
|
+
if (typeof token === "function") (token = token());
|
|
28
|
+
|
|
29
|
+
const input = (typeof inputs[token] === "function") ? inputs[token]() : inputs[token];
|
|
30
|
+
const field = (typeof fields[token] === "function") ? fields[token]() : fields[token];
|
|
31
|
+
const statement = (typeof statements[token] === "function") ? statements[token]() : statements[token];
|
|
32
|
+
|
|
33
|
+
if (input) {
|
|
34
|
+
if ((typeof input.check === "function") && ![String, Number, Boolean, Array, Object].includes(input.check)) (input.check = input.check());
|
|
35
|
+
|
|
36
|
+
const valueInput = this.appendValueInput(token)
|
|
37
|
+
|
|
38
|
+
if (input.check) {
|
|
39
|
+
if (!Array.isArray(input.check)) (input.check = [input.check]);
|
|
40
|
+
|
|
41
|
+
input.check = input.check.map((check) => [null, "null"].includes(check) ? "Null" : ((check === "undefined") ? "Undefined" : ([String, Number, Boolean, Array, Object].includes(check) ? check.name : check)));
|
|
42
|
+
|
|
43
|
+
valueInput.setCheck(input.check || null);
|
|
44
|
+
} else {
|
|
45
|
+
valueInput.connection.setShadowDom(new DOMParser().parseFromString(`<shadow type="string"><field name="TEXT"></field></shadow>`, "text/xml").firstChild);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (input.shadow) {
|
|
49
|
+
if (typeof input.shadow === "function") (input.shadow = input.shadow());
|
|
50
|
+
|
|
51
|
+
valueInput.connection.setShadowDom(new DOMParser().parseFromString(`<shadow type="${((typeof input.shadow.type === "function") ? input.shadow.type() : (input.shadow.type || "text")).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'")}">${(typeof input.shadow.content === "function") ? input.shadow.content() : (input.shadow.content || "")}</shadow>`, "text/xml").firstChild);
|
|
52
|
+
};
|
|
53
|
+
} else if (field) {
|
|
54
|
+
const dummy = this.appendDummyInput();
|
|
55
|
+
|
|
56
|
+
switch (field.type) {
|
|
57
|
+
case "text":
|
|
58
|
+
dummy.appendField(new Blockly.FieldTextInput(...[
|
|
59
|
+
translate((typeof field.default === "function") ? field.default() : (field.default || "")),
|
|
60
|
+
...(field.validator) ? [field.validator] : []
|
|
61
|
+
]), token);
|
|
62
|
+
break;
|
|
63
|
+
case "multiline":
|
|
64
|
+
dummy.appendField(new Blockly.FieldMultilineInput(...[
|
|
65
|
+
translate((typeof field.default === "function") ? field.default() : (field.default || "")),
|
|
66
|
+
...(field.validator) ? [field.validator] : []
|
|
67
|
+
]), token);
|
|
68
|
+
break;
|
|
69
|
+
case "dropdown":
|
|
70
|
+
dummy.appendField(new Blockly.FieldDropdown(...[
|
|
71
|
+
Object.entries(field.options).map(([name, text]) => [translate((typeof text === "function") ? text() : text), (typeof name === "function") ? name() : name]),
|
|
72
|
+
...(field.validator) ? [field.validator] : []
|
|
73
|
+
]), token);
|
|
74
|
+
break;
|
|
75
|
+
case "variable":
|
|
76
|
+
dummy.appendField(new Blockly.FieldVariable(...[
|
|
77
|
+
translate((typeof field.default === "function") ? field.default() : (field.default || "")),
|
|
78
|
+
...(field.validator) ? [field.validator] : []
|
|
79
|
+
]), token);
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
dummy.appendField(translate(token));
|
|
83
|
+
};
|
|
84
|
+
} else if (statement) {
|
|
85
|
+
const statementInput = this.appendStatementInput(token).setCheck(null);
|
|
86
|
+
|
|
87
|
+
if (statement.label) statementInput.appendField(translate((typeof statement.label === "function") ? statement.label() : statement.label));
|
|
41
88
|
} else {
|
|
42
|
-
|
|
89
|
+
this.appendDummyInput().appendField(translate(token));
|
|
43
90
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Object.entries(field.options).map(([name, text]) => [translate((typeof text === "function") ? text() : text), (typeof name === "function") ? name() : name]),
|
|
69
|
-
...(field.validator) ? [field.validator] : []
|
|
70
|
-
]), token);
|
|
71
|
-
break;
|
|
72
|
-
case "variable":
|
|
73
|
-
dummy.appendField(new Blockly.FieldVariable(...[
|
|
74
|
-
translate((typeof field.default === "function") ? field.default() : (field.default || "")),
|
|
75
|
-
...(field.validator) ? [field.validator] : []
|
|
76
|
-
]), token);
|
|
77
|
-
break;
|
|
78
|
-
default:
|
|
79
|
-
dummy.appendField(translate(token));
|
|
80
|
-
};
|
|
81
|
-
} else if (statement) {
|
|
82
|
-
const statementInput = this.appendStatementInput(token).setCheck(null);
|
|
83
|
-
|
|
84
|
-
if (statement.label) statementInput.appendField(translate((typeof statement.label === "function") ? statement.label() : statement.label));
|
|
85
|
-
} else {
|
|
86
|
-
this.appendDummyInput().appendField(translate(token));
|
|
87
|
-
};
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (!output) {
|
|
95
|
+
if ((typeof previous === "function") ? previous() : previous) this.setPreviousStatement(true, null);
|
|
96
|
+
if ((typeof next === "function") ? next() : next) this.setNextStatement(true, null);
|
|
97
|
+
} else {
|
|
98
|
+
if ((typeof output === "function") && ![String, Number, Boolean, Array, Object].includes(output)) (output = output());
|
|
99
|
+
|
|
100
|
+
this.setPreviousStatement(false);
|
|
101
|
+
this.setNextStatement(false);
|
|
102
|
+
this.setOutput(true, (output === true) ? null : (([String, Number, Boolean, Array, Object].includes(output)) ? output.name : output));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
this.setColour((color != null) ? ((typeof color === "function") ? color() : color) : (Blockly.getMainWorkspace().getToolbox().contents_.find((category) => category.toolboxItemDef_.contents?.some((block) => block.type === name))?.toolboxItemDef_?.colour || "#000000"));
|
|
106
|
+
this.setInputsInline((typeof inline === "function") ? inline() : inline);
|
|
107
|
+
this.setDeletable((typeof deletable === "function") ? deletable() : deletable);
|
|
108
|
+
this.setTooltip(translate((typeof tooltip === "function") ? tooltip() : tooltip));
|
|
109
|
+
this.setHelpUrl((typeof help === "function") ? help() : help);
|
|
110
|
+
|
|
111
|
+
this.inputList.forEach((input) => {
|
|
112
|
+
if (input.connection?.shadowDom || !Array.isArray(input.connection?.check) || input.connection.check.every((check) => !["String", "Number"].includes(check))) return;
|
|
113
|
+
|
|
114
|
+
input.connection.setShadowDom(new DOMParser().parseFromString(`<shadow type="${((input.connection.check?.length === 1) && (input.connection.check[0] === "Number")) ? "number" : "string"}"><field name="${((input.connection.check?.length === 1) && (input.connection.check[0] === "Number")) ? "NUMBER" : "TEXT"}"></field></shadow>`, "text/xml").firstChild);
|
|
88
115
|
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
Object.entries({
|
|
120
|
+
...blockGenerators,
|
|
121
|
+
...(blockGeneratorFallback) ? {
|
|
122
|
+
Fallback: blockGeneratorFallback
|
|
123
|
+
} : {}
|
|
124
|
+
}).forEach(([languageName, blockGenerator]) => {
|
|
125
|
+
if (![
|
|
126
|
+
...Object.keys(languageGenerators),
|
|
127
|
+
"Fallback"
|
|
128
|
+
].includes(languageName)) return;
|
|
129
|
+
|
|
130
|
+
const languageGenerator = ({
|
|
131
|
+
...Object.fromEntries([
|
|
132
|
+
Blockly.JavaScript,
|
|
133
|
+
Blockly.Python,
|
|
134
|
+
Blockly.Lua,
|
|
135
|
+
Blockly.PHP
|
|
136
|
+
].filter(Boolean).map((codeGenerator) => [codeGenerator.name_, codeGenerator])),
|
|
137
|
+
...languageGenerators,
|
|
138
|
+
...{
|
|
139
|
+
Fallback: languageGeneratorFallback
|
|
140
|
+
}
|
|
141
|
+
})[languageName];
|
|
142
|
+
|
|
143
|
+
if (!languageGenerator) return;
|
|
144
|
+
|
|
145
|
+
languageGenerator.forBlock[name] = (block) => blockGenerator({
|
|
146
|
+
...block.inputList.reduce((accumulator, input) => ({
|
|
147
|
+
...accumulator,
|
|
148
|
+
...Object.fromEntries(input.fieldRow.map((field) => [field.name, block.getFieldValue(field.name)])),
|
|
149
|
+
...(input.name) ? {
|
|
150
|
+
[input.name]: ({
|
|
151
|
+
[Blockly.INPUT_VALUE]: languageGenerator.valueToCode(block, input.name, languageGenerator.ORDER_ATOMIC),
|
|
152
|
+
[Blockly.NEXT_STATEMENT]: languageGenerator.statementToCode(block, input.name)
|
|
153
|
+
})[input.type] ?? null
|
|
154
|
+
} : {}
|
|
155
|
+
}), {}),
|
|
156
|
+
});
|
|
157
|
+
});
|
|
101
158
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.setDeletable((typeof deletable === "function") ? deletable() : deletable);
|
|
105
|
-
this.setTooltip(translate((typeof tooltip === "function") ? tooltip() : tooltip));
|
|
106
|
-
this.setHelpUrl((typeof help === "function") ? help() : help);
|
|
159
|
+
return true;
|
|
160
|
+
},
|
|
107
161
|
|
|
108
|
-
|
|
109
|
-
|
|
162
|
+
get(_, name) {
|
|
163
|
+
return Blockly.Blocks[name];
|
|
164
|
+
}
|
|
165
|
+
});
|
|
110
166
|
|
|
111
|
-
|
|
112
|
-
|
|
167
|
+
Blocks["string"] = {
|
|
168
|
+
layout: "TEXT",
|
|
169
|
+
fields: {
|
|
170
|
+
TEXT: {
|
|
171
|
+
type: "text"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
output: true,
|
|
175
|
+
|
|
176
|
+
generator({ TEXT = `""` }) {
|
|
177
|
+
return `"${TEXT}"`;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
Blocks["number"] = {
|
|
182
|
+
layout: "NUMBER",
|
|
183
|
+
fields: {
|
|
184
|
+
NUMBER: {
|
|
185
|
+
type: "text",
|
|
186
|
+
default: "0",
|
|
187
|
+
validator: (value) => {
|
|
188
|
+
if (!value) return "0";
|
|
189
|
+
if (/^-?\d*\.?\d*$/.test(value)) return value;
|
|
190
|
+
}
|
|
113
191
|
}
|
|
114
|
-
}
|
|
192
|
+
},
|
|
193
|
+
output: Number,
|
|
115
194
|
|
|
116
|
-
|
|
117
|
-
|
|
195
|
+
generator({ NUMBER = 0 }) {
|
|
196
|
+
return `${parseFloat(NUMBER)}`;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
118
199
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
});
|
|
200
|
+
return Blocks;
|
|
201
|
+
};
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blockly-fluid",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A flexible proxy wrapper for Blockly blocks.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
8
|
},
|
|
9
|
-
"keywords": [
|
|
9
|
+
"keywords": [
|
|
10
|
+
"blockly",
|
|
11
|
+
"blocks",
|
|
12
|
+
"fluid",
|
|
13
|
+
"proxy",
|
|
14
|
+
"wrapper"
|
|
15
|
+
],
|
|
10
16
|
"author": "DinoscapeProgramming",
|
|
11
17
|
"license": "MIT",
|
|
12
18
|
"type": "commonjs"
|