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.
Files changed (4) hide show
  1. package/README.md +6 -2
  2. package/index.js +192 -113
  3. package/index.ts +1 -0
  4. 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 } = {}) => new Proxy({}, {
2
- set(_, name, {
3
- inputs = {},
4
- layout = [],
5
- fields = {},
6
- statements = {},
7
- previous = true,
8
- next = true,
9
- output,
10
- color,
11
- inline = true,
12
- deletable = true,
13
- tooltip = "",
14
- help = ""
15
- }) {
16
- Blockly.Blocks[name] = {
17
- init: function () {
18
- if (typeof inputs === "function") (inputs = inputs());
19
- if (typeof layout === "function") (layout = layout());
20
- if (typeof statements === "function") (statements = statements());
21
-
22
- if ((Array.isArray(layout)) ? layout.length : ((typeof layout === "string") ? layout : false)) {
23
- ((Array.isArray(layout)) ? layout : [layout]).forEach((token) => {
24
- if (typeof token === "function") (token = token());
25
-
26
- const input = (typeof inputs[token] === "function") ? inputs[token]() : inputs[token];
27
- const field = (typeof fields[token] === "function") ? fields[token]() : fields[token];
28
- const statement = (typeof statements[token] === "function") ? statements[token]() : statements[token];
29
-
30
- if (input) {
31
- if ((typeof input.check === "function") && ![String, Number, Boolean, Array, Object].includes(input.check)) (input.check = input.check());
32
-
33
- const valueInput = this.appendValueInput(token)
34
-
35
- if (input.check) {
36
- if (!Array.isArray(input.check)) (input.check = [input.check]);
37
-
38
- input.check = input.check.map((check) => [null, "null"].includes(check) ? "Null" : ((check === "undefined") ? "Undefined" : ([String, Number, Boolean, Array, Object].includes(check) ? check.name : check)));
39
-
40
- valueInput.setCheck(input.check || null);
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;")}">${(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
- valueInput.connection.setShadowDom(new DOMParser().parseFromString(`<shadow type="string"><field name="TEXT"></field></shadow>`, "text/xml").firstChild);
89
+ this.appendDummyInput().appendField(translate(token));
43
90
  };
44
-
45
- if (input.shadow) {
46
- if (typeof input.shadow === "function") (input.shadow = input.shadow());
47
-
48
- valueInput.connection.setShadowDom(new DOMParser().parseFromString(`<shadow type="${((typeof input.shadow.type === "function") ? input.shadow.type() : (input.shadow.type || "text")).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;")}">${(typeof input.shadow.content === "function") ? input.shadow.content() : (input.shadow.content || "")}</shadow>`, "text/xml").firstChild);
49
- };
50
- } else if (field) {
51
- const dummy = this.appendDummyInput();
52
-
53
- switch (field.type) {
54
- case "text":
55
- dummy.appendField(new Blockly.FieldTextInput(...[
56
- translate((typeof field.default === "function") ? field.default() : (field.default || "")),
57
- ...(field.validator) ? [field.validator] : []
58
- ]), token);
59
- break;
60
- case "multiline":
61
- dummy.appendField(new Blockly.FieldMultilineInput(...[
62
- translate((typeof field.default === "function") ? field.default() : (field.default || "")),
63
- ...(field.validator) ? [field.validator] : []
64
- ]), token);
65
- break;
66
- case "dropdown":
67
- dummy.appendField(new Blockly.FieldDropdown(...[
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
- if (!output) {
92
- if ((typeof previous === "function") ? previous() : previous) this.setPreviousStatement(true, null);
93
- if ((typeof next === "function") ? next() : next) this.setNextStatement(true, null);
94
- } else {
95
- if ((typeof output === "function") && ![String, Number, Boolean, Array, Object].includes(output)) (output = output());
96
-
97
- this.setPreviousStatement(false);
98
- this.setNextStatement(false);
99
- this.setOutput(true, (output === true) ? null : (([String, Number, Boolean, Array, Object].includes(output)) ? output.name : output));
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
- 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"));
103
- this.setInputsInline((typeof inline === "function") ? inline() : inline);
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
- this.inputList.forEach((input) => {
109
- if (input.connection?.shadowDom || !Array.isArray(input.connection?.check) || input.connection.check.every((check) => !["String", "Number"].includes(check))) return;
162
+ get(_, name) {
163
+ return Blockly.Blocks[name];
164
+ }
165
+ });
110
166
 
111
- 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);
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
- return true;
117
- },
195
+ generator({ NUMBER = 0 }) {
196
+ return `${parseFloat(NUMBER)}`;
197
+ }
198
+ };
118
199
 
119
- get(_, name) {
120
- return Blockly.Blocks[name];
121
- }
122
- });
200
+ return Blocks;
201
+ };
package/index.ts CHANGED
@@ -30,6 +30,7 @@ type BlockDefinition = {
30
30
  deletable?: boolean | Function;
31
31
  tooltip?: string | Function;
32
32
  help?: string | Function;
33
+ generator?: Function;
33
34
  };
34
35
 
35
36
  interface BlocklyProxy {
package/package.json CHANGED
@@ -1,12 +1,18 @@
1
1
  {
2
2
  "name": "blockly-fluid",
3
- "version": "1.0.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"