@adcops/autocore-react 3.0.22 → 3.0.25
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/dist/components/BlocklyEditor.d.ts +51 -53
- package/dist/components/BlocklyEditor.js +1 -1
- package/dist/components/TextInput.d.ts +2 -1
- package/dist/components/TextInput.js +1 -1
- package/dist/components/ValueInput.d.ts +2 -1
- package/dist/components/ValueInput.js +1 -1
- package/dist/hooks/adsHooks.d.ts +1 -1
- package/dist/hooks/adsHooks.js +1 -1
- package/package.json +1 -1
- package/src/components/BlocklyEditor.tsx +154 -143
- package/src/components/TextInput.tsx +6 -3
- package/src/components/ValueInput.tsx +6 -4
- package/src/hooks/adsHooks.tsx +9 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Blockly from "blockly";
|
|
2
1
|
import React from 'react';
|
|
2
|
+
import Blockly from "blockly";
|
|
3
3
|
import "./BlocklyEditor.css";
|
|
4
4
|
/**
|
|
5
5
|
* Properties for the Blockly Editor.
|
|
@@ -9,7 +9,7 @@ interface BlocklyEditorProps {
|
|
|
9
9
|
* The initial XML representation of the Blockly workspace. Can be undefined
|
|
10
10
|
* if the workspace should start empty.
|
|
11
11
|
*/
|
|
12
|
-
initialXml
|
|
12
|
+
initialXml?: string | undefined;
|
|
13
13
|
/**
|
|
14
14
|
* The configuration definition for the Blockly Toolbox.
|
|
15
15
|
*/
|
|
@@ -30,7 +30,7 @@ interface BlocklyEditorProps {
|
|
|
30
30
|
* Callback fired when the Blockly workspace content changes.
|
|
31
31
|
* Note: This does not directly reflect changes to the XML or the code.
|
|
32
32
|
*/
|
|
33
|
-
onWorkspaceChanged?: () => void;
|
|
33
|
+
onWorkspaceChanged?: (workspace: Blockly.WorkspaceSvg | undefined) => void;
|
|
34
34
|
/**
|
|
35
35
|
* Callback fired when the XML representation of the workspace changes.
|
|
36
36
|
* @param xml The updated XML representation of the workspace.
|
|
@@ -41,35 +41,11 @@ interface BlocklyEditorProps {
|
|
|
41
41
|
* @param code The updated generated code.
|
|
42
42
|
*/
|
|
43
43
|
onCodeChanged?: (code: string) => void;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* State of the BlocklyEditor
|
|
47
|
-
*/
|
|
48
|
-
interface BlocklyEditorState {
|
|
49
|
-
/**
|
|
50
|
-
* A name for the Blockly editor, potentially used for identification.
|
|
51
|
-
*/
|
|
52
|
-
name: string;
|
|
53
|
-
/**
|
|
54
|
-
* The current XML representation of the workspace.
|
|
55
|
-
*/
|
|
56
|
-
xml: string;
|
|
57
44
|
/**
|
|
58
|
-
*
|
|
45
|
+
* Callback fired when an error occurs.
|
|
46
|
+
* Includes an error message.
|
|
59
47
|
*/
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* The height of the first parent container with
|
|
63
|
-
* classname "blockly-container". It is important to set
|
|
64
|
-
* a parent container with this class name for auto-sizing to work!
|
|
65
|
-
*/
|
|
66
|
-
parentHeight: number;
|
|
67
|
-
/**
|
|
68
|
-
* The width of the first parent container with
|
|
69
|
-
* classname "blockly-container". It is important to set
|
|
70
|
-
* a parent container with this class name for auto-sizing to work!
|
|
71
|
-
*/
|
|
72
|
-
parentWidth: number;
|
|
48
|
+
onError?: (message: string) => void;
|
|
73
49
|
}
|
|
74
50
|
/**
|
|
75
51
|
* A view that contains the blockly editor, wrapping the react-blockly BlocklyWorkspace
|
|
@@ -83,30 +59,52 @@ interface BlocklyEditorState {
|
|
|
83
59
|
*
|
|
84
60
|
* Right now, the editor only supports generating python code, but a feature to specify the
|
|
85
61
|
* generator for different languages is planned.
|
|
62
|
+
*
|
|
63
|
+
* ## Usage Example
|
|
64
|
+
*
|
|
65
|
+
* ```tsx
|
|
66
|
+
* import React, { useRef } from 'react';
|
|
67
|
+
* import BlocklyEditor from './BlocklyEditor';
|
|
68
|
+
* import { StandardToolbox } from './toolbox';
|
|
69
|
+
*
|
|
70
|
+
* const App: React.FC = () => {
|
|
71
|
+
* const blocklyEditorRef = useRef<{ newBlockly: () => void; loadBlockly: (xmlString: string) => void }>(null);
|
|
72
|
+
*
|
|
73
|
+
* const handleNew = () => {
|
|
74
|
+
* if (blocklyEditorRef.current) {
|
|
75
|
+
* blocklyEditorRef.current.newBlockly();
|
|
76
|
+
* }
|
|
77
|
+
* };
|
|
78
|
+
*
|
|
79
|
+
* const handleLoad = (xmlString: string) => {
|
|
80
|
+
* if (blocklyEditorRef.current) {
|
|
81
|
+
* blocklyEditorRef.current.loadBlockly(xmlString);
|
|
82
|
+
* }
|
|
83
|
+
* };
|
|
84
|
+
*
|
|
85
|
+
* return (
|
|
86
|
+
* <div className="blockly-container" style={{ height: '500px' }}>
|
|
87
|
+
* <BlocklyEditor
|
|
88
|
+
* ref={blocklyEditorRef}
|
|
89
|
+
* initialXml="<xml xmlns='http://www.w3.org/1999/xhtml'></xml>"
|
|
90
|
+
* toolbox={StandardToolbox}
|
|
91
|
+
* widthAdjustment={0}
|
|
92
|
+
* heightAdjustment={0}
|
|
93
|
+
* onWorkspaceChanged={(workspace) => console.log('Workspace changed:', workspace)}
|
|
94
|
+
* onXmlChanged={(xml) => console.log('XML changed:', xml)}
|
|
95
|
+
* onCodeChanged={(code) => console.log('Code changed:', code)}
|
|
96
|
+
* onError={(message) => console.error('Error:', message)}
|
|
97
|
+
* />
|
|
98
|
+
* <button onClick={handleNew}>New</button>
|
|
99
|
+
* <button onClick={() => handleLoad('<xml xmlns="http://www.w3.org/1999/xhtml"><block type="controls_if" x="10" y="10"></block></xml>')}>Load</button>
|
|
100
|
+
* </div>
|
|
101
|
+
* );
|
|
102
|
+
* };
|
|
103
|
+
*
|
|
104
|
+
* export default App;
|
|
105
|
+
* ```
|
|
86
106
|
*/
|
|
87
|
-
export declare
|
|
88
|
-
static defaultProps: {
|
|
89
|
-
initialXml: undefined;
|
|
90
|
-
widthAdjustment: number;
|
|
91
|
-
heightAdjustment: number;
|
|
92
|
-
onWorkspaceChanged: undefined;
|
|
93
|
-
onXmlChanged: undefined;
|
|
94
|
-
onCodeChanged: undefined;
|
|
95
|
-
};
|
|
96
|
-
private ref;
|
|
97
|
-
/**
|
|
98
|
-
* Constructor
|
|
99
|
-
*/
|
|
100
|
-
constructor(props: BlocklyEditorProps);
|
|
101
|
-
componentDidMount(): void;
|
|
102
|
-
componentDidUpdate(prevProps: BlocklyEditorProps): void;
|
|
103
|
-
componentWillUnmount(): void;
|
|
104
|
-
updateParentSize: () => void;
|
|
105
|
-
private onWorkspaceChanged;
|
|
106
|
-
private setXml;
|
|
107
|
-
private onInject;
|
|
108
|
-
render(): import("react/jsx-runtime").JSX.Element;
|
|
109
|
-
}
|
|
107
|
+
export declare const BlocklyEditor: React.ForwardRefExoticComponent<BlocklyEditorProps & React.RefAttributes<unknown>>;
|
|
110
108
|
export default BlocklyEditor;
|
|
111
109
|
/**
|
|
112
110
|
* Creates a custom Blockly toolbox XML string by inserting additional XML before and after
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,Fragment as _Fragment}from"react/jsx-runtime";import{pythonGenerator}from"blockly/python";import{BlocklyWorkspace}from"react-blockly";import React from"react";import"./BlocklyEditor.css";export class BlocklyEditor extends React.Component{constructor(e){super(e),Object.defineProperty(this,"ref",{enumerable:!0,configurable:!0,writable:!0,value:React.createRef()}),Object.defineProperty(this,"updateParentSize",{enumerable:!0,configurable:!0,writable:!0,value:()=>{if(this.ref.current){let e=0,n=0,t=this.ref.current.parentElement;for(;null!=t&&(e=t.offsetWidth,n=t.offsetHeight,!(t.className.indexOf("blockly-container")>=0));)t=t.parentElement;e<300&&(e=300),n<300&&(n=300),this.setState({parentWidth:.9*e+this.props.widthAdjustment,parentHeight:.9*n+this.props.heightAdjustment})}}}),Object.defineProperty(this,"onWorkspaceChanged",{enumerable:!0,configurable:!0,writable:!0,value:e=>{const n=pythonGenerator.workspaceToCode(e);this.setState({code:n}),void 0!==this.props.onWorkspaceChanged&&null!==this.props.onWorkspaceChanged&&this.props.onWorkspaceChanged(),void 0!==this.props.onCodeChanged&&null!==this.props.onCodeChanged&&this.props.onCodeChanged(n)}}),Object.defineProperty(this,"setXml",{enumerable:!0,configurable:!0,writable:!0,value:e=>{this.setState({xml:e}),void 0!==this.props.onXmlChanged&&null!==this.props.onXmlChanged&&this.props.onXmlChanged(e)}}),Object.defineProperty(this,"onInject",{enumerable:!0,configurable:!0,writable:!0,value:e=>{}}),this.state={name:"",xml:"",code:"",parentWidth:0,parentHeight:0}}componentDidMount(){this.updateParentSize(),window.addEventListener("resize",this.updateParentSize)}componentDidUpdate(e){}componentWillUnmount(){window.removeEventListener("resize",this.updateParentSize)}render(){return _jsx(_Fragment,{children:_jsx(BlocklyWorkspace,{className:"fill-height",toolboxConfiguration:this.props.toolbox,initialXml:this.props.initialXml,onWorkspaceChange:this.onWorkspaceChanged,onXmlChange:this.setXml,onInject:this.onInject,workspaceConfiguration:{grid:{spacing:20,length:3,colour:"#ccc",snap:!0}}})})}}Object.defineProperty(BlocklyEditor,"defaultProps",{enumerable:!0,configurable:!0,writable:!0,value:{initialXml:void 0,widthAdjustment:0,heightAdjustment:0,onWorkspaceChanged:void 0,onXmlChanged:void 0,onCodeChanged:void 0}});export default BlocklyEditor;export const createCustomToolbox=(e,n)=>{const t="\x3c!-- END: Built-in Blockly Blocks //--\x3e",a=StandardToolbox.indexOf("\x3c!-- BEGIN: Built-in Blockly Blocks //--\x3e"),l=StandardToolbox.indexOf(t);if(-1===a||-1===l)throw new Error("Could not find markers in standard toolbox");return StandardToolbox.slice(0,a)+(e||"")+StandardToolbox.slice(a,l+39)+(n||"")+StandardToolbox.slice(l+39+1)};export const StandardToolbox='\n <xml id="toolbox" style="display: none">\n \x3c!-- BEGIN: Built-in Blockly Blocks //--\x3e\n <category name="Logic" categorystyle="logic_category">\n <block type="controls_if"></block>\n <block type="logic_compare"></block>\n <block type="logic_operation"></block>\n <block type="logic_negate"></block>\n <block type="logic_boolean"></block>\n <block type="logic_null" disabled="true"></block>\n <block type="logic_ternary"></block>\n </category>\n <category name="Loops" categorystyle="loop_category">\n <block type="controls_repeat_ext">\n <value name="TIMES">\n <shadow type="math_number">\n <field name="NUM">10</field>\n </shadow>\n </value>\n </block>\n <block type="controls_repeat" disabled="true"></block>\n <block type="controls_whileUntil"></block>\n <block type="controls_for">\n <value name="FROM">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="TO">\n <shadow type="math_number">\n <field name="NUM">10</field>\n </shadow>\n </value>\n <value name="BY">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n </block>\n <block type="controls_forEach"></block>\n <block type="controls_flow_statements"></block>\n </category>\n <category name="Math" categorystyle="math_category">\n <block type="math_number" gap="32">\n <field name="NUM">123</field>\n </block>\n <block type="math_arithmetic">\n <value name="A">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="B">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n </block>\n <block type="math_single">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">9</field>\n </shadow>\n </value>\n </block>\n <block type="math_trig">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">45</field>\n </shadow>\n </value>\n </block>\n <block type="math_constant"></block>\n <block type="math_number_property">\n <value name="NUMBER_TO_CHECK">\n <shadow type="math_number">\n <field name="NUM">0</field>\n </shadow>\n </value>\n </block>\n <block type="math_round">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">3.1</field>\n </shadow>\n </value>\n </block>\n <block type="math_on_list"></block>\n <block type="math_modulo">\n <value name="DIVIDEND">\n <shadow type="math_number">\n <field name="NUM">64</field>\n </shadow>\n </value>\n <value name="DIVISOR">\n <shadow type="math_number">\n <field name="NUM">10</field>\n </shadow>\n </value>\n </block>\n <block type="math_constrain">\n <value name="VALUE">\n <shadow type="math_number">\n <field name="NUM">50</field>\n </shadow>\n </value>\n <value name="LOW">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="HIGH">\n <shadow type="math_number">\n <field name="NUM">100</field>\n </shadow>\n </value>\n </block>\n <block type="math_random_int">\n <value name="FROM">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="TO">\n <shadow type="math_number">\n <field name="NUM">100</field>\n </shadow>\n </value>\n </block>\n <block type="math_random_float"></block>\n <block type="math_atan2">\n <value name="X">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="Y">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n </block>\n </category>\n <category name="Text" categorystyle="text_category">\n <block type="text"></block>\n <block type="text_multiline"></block>\n <block type="text_join"></block>\n <block type="text_append">\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <block type="text_length">\n <value name="VALUE">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_isEmpty">\n <value name="VALUE">\n <shadow type="text">\n <field name="TEXT"></field>\n </shadow>\n </value>\n </block>\n <block type="text_indexOf">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">text</field>\n </block>\n </value>\n <value name="FIND">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_charAt">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">text</field>\n </block>\n </value>\n </block>\n <block type="text_getSubstring">\n <value name="STRING">\n <block type="variables_get">\n <field name="VAR">text</field>\n </block>\n </value>\n </block>\n <block type="text_changeCase">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_trim">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_count">\n <value name="SUB">\n <shadow type="text"></shadow>\n </value>\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <block type="text_replace">\n <value name="FROM">\n <shadow type="text"></shadow>\n </value>\n <value name="TO">\n <shadow type="text"></shadow>\n </value>\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <block type="text_reverse">\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <label text="Input/Output:" web-class="ioLabel"></label>\n <block type="text_print">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_prompt_ext">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n </category>\n <category name="Lists" categorystyle="list_category">\n <block type="lists_create_with">\n <mutation items="0"></mutation>\n </block>\n <block type="lists_create_with"></block>\n <block type="lists_repeat">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">5</field>\n </shadow>\n </value>\n </block>\n <block type="lists_length"></block>\n <block type="lists_isEmpty"></block>\n <block type="lists_indexOf">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_getIndex">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_setIndex">\n <value name="LIST">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_getSublist">\n <value name="LIST">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_split">\n <value name="DELIM">\n <shadow type="text">\n <field name="TEXT">,</field>\n </shadow>\n </value>\n </block>\n <block type="lists_sort"></block>\n <block type="lists_reverse"></block>\n </category>\n <category name="Variables" categorystyle="variable_category" custom="VARIABLE"></category>\n <category name="Functions" categorystyle="procedure_category" custom="PROCEDURE"></category>\n\n \x3c!-- END: Built-in Blockly Blocks //--\x3e\n\n </xml>\n';
|
|
1
|
+
import{jsx as _jsx}from"react/jsx-runtime";import React,{forwardRef,useImperativeHandle,useCallback,useState}from"react";import Blockly from"blockly";import{pythonGenerator}from"blockly/python";import{BlocklyWorkspace}from"react-blockly";import"./BlocklyEditor.css";export const BlocklyEditor=forwardRef(((e,n)=>{const[l,t]=useState(""),[a,o]=useState(""),[c,d]=useState(0),[s,i]=useState(0),[m,r]=useState(void 0),b=React.createRef();useImperativeHandle(n,(()=>({newBlockly(){m&&m.clear()},loadBlockly(n){if(m)try{m.clear();const e=Blockly.utils.xml.textToDom(n);Blockly.Xml.domToWorkspace(e,m)}catch(n){e.onError&&e.onError(`${n}`)}}}))),React.useEffect((()=>(y(),window.addEventListener("resize",y),()=>{window.removeEventListener("resize",y)})),[]);const y=useCallback((()=>{if(b.current){let n=0,l=0,t=b.current.parentElement;for(;null!==t&&(n=t.offsetWidth,l=t.offsetHeight,!(t.className.indexOf("blockly-container")>=0));)t=t.parentElement;n<300&&(n=300),l<300&&(l=300),d(.9*n+e.widthAdjustment),i(.9*l+e.heightAdjustment)}}),[e.widthAdjustment,e.heightAdjustment]);return _jsx("div",{ref:b,style:{width:c,height:s},children:_jsx(BlocklyWorkspace,{className:"fill-height",toolboxConfiguration:e.toolbox,initialXml:e.initialXml,onWorkspaceChange:n=>{const l=pythonGenerator.workspaceToCode(n);o(l),e.onWorkspaceChanged&&e.onWorkspaceChanged(n),e.onCodeChanged&&e.onCodeChanged(l)},onXmlChange:n=>{t(n),void 0!==e.onXmlChanged&&null!==e.onXmlChanged&&e.onXmlChanged(n)},onInject:e=>{r(e)},workspaceConfiguration:{grid:{spacing:20,length:3,colour:"#ccc",snap:!0}}})})}));export default BlocklyEditor;export const createCustomToolbox=(e,n)=>{const l="\x3c!-- END: Built-in Blockly Blocks //--\x3e",t=StandardToolbox.indexOf("\x3c!-- BEGIN: Built-in Blockly Blocks //--\x3e"),a=StandardToolbox.indexOf(l);if(-1===t||-1===a)throw new Error("Could not find markers in standard toolbox");return StandardToolbox.slice(0,t)+(e||"")+StandardToolbox.slice(t,a+39)+(n||"")+StandardToolbox.slice(a+39+1)};export const StandardToolbox='\n <xml id="toolbox" style="display: none">\n \x3c!-- BEGIN: Built-in Blockly Blocks //--\x3e\n <category name="Logic" categorystyle="logic_category">\n <block type="controls_if"></block>\n <block type="logic_compare"></block>\n <block type="logic_operation"></block>\n <block type="logic_negate"></block>\n <block type="logic_boolean"></block>\n <block type="logic_null" disabled="true"></block>\n <block type="logic_ternary"></block>\n </category>\n <category name="Loops" categorystyle="loop_category">\n <block type="controls_repeat_ext">\n <value name="TIMES">\n <shadow type="math_number">\n <field name="NUM">10</field>\n </shadow>\n </value>\n </block>\n <block type="controls_repeat" disabled="true"></block>\n <block type="controls_whileUntil"></block>\n <block type="controls_for">\n <value name="FROM">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="TO">\n <shadow type="math_number">\n <field name="NUM">10</field>\n </shadow>\n </value>\n <value name="BY">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n </block>\n <block type="controls_forEach"></block>\n <block type="controls_flow_statements"></block>\n </category>\n <category name="Math" categorystyle="math_category">\n <block type="math_number" gap="32">\n <field name="NUM">123</field>\n </block>\n <block type="math_arithmetic">\n <value name="A">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="B">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n </block>\n <block type="math_single">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">9</field>\n </shadow>\n </value>\n </block>\n <block type="math_trig">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">45</field>\n </shadow>\n </value>\n </block>\n <block type="math_constant"></block>\n <block type="math_number_property">\n <value name="NUMBER_TO_CHECK">\n <shadow type="math_number">\n <field name="NUM">0</field>\n </shadow>\n </value>\n </block>\n <block type="math_round">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">3.1</field>\n </shadow>\n </value>\n </block>\n <block type="math_on_list"></block>\n <block type="math_modulo">\n <value name="DIVIDEND">\n <shadow type="math_number">\n <field name="NUM">64</field>\n </shadow>\n </value>\n <value name="DIVISOR">\n <shadow type="math_number">\n <field name="NUM">10</field>\n </shadow>\n </value>\n </block>\n <block type="math_constrain">\n <value name="VALUE">\n <shadow type="math_number">\n <field name="NUM">50</field>\n </shadow>\n </value>\n <value name="LOW">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="HIGH">\n <shadow type="math_number">\n <field name="NUM">100</field>\n </shadow>\n </value>\n </block>\n <block type="math_random_int">\n <value name="FROM">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="TO">\n <shadow type="math_number">\n <field name="NUM">100</field>\n </shadow>\n </value>\n </block>\n <block type="math_random_float"></block>\n <block type="math_atan2">\n <value name="X">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n <value name="Y">\n <shadow type="math_number">\n <field name="NUM">1</field>\n </shadow>\n </value>\n </block>\n </category>\n <category name="Text" categorystyle="text_category">\n <block type="text"></block>\n <block type="text_multiline"></block>\n <block type="text_join"></block>\n <block type="text_append">\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <block type="text_length">\n <value name="VALUE">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_isEmpty">\n <value name="VALUE">\n <shadow type="text">\n <field name="TEXT"></field>\n </shadow>\n </value>\n </block>\n <block type="text_indexOf">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">text</field>\n </block>\n </value>\n <value name="FIND">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_charAt">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">text</field>\n </block>\n </value>\n </block>\n <block type="text_getSubstring">\n <value name="STRING">\n <block type="variables_get">\n <field name="VAR">text</field>\n </block>\n </value>\n </block>\n <block type="text_changeCase">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_trim">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_count">\n <value name="SUB">\n <shadow type="text"></shadow>\n </value>\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <block type="text_replace">\n <value name="FROM">\n <shadow type="text"></shadow>\n </value>\n <value name="TO">\n <shadow type="text"></shadow>\n </value>\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <block type="text_reverse">\n <value name="TEXT">\n <shadow type="text"></shadow>\n </value>\n </block>\n <label text="Input/Output:" web-class="ioLabel"></label>\n <block type="text_print">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n <block type="text_prompt_ext">\n <value name="TEXT">\n <shadow type="text">\n <field name="TEXT">abc</field>\n </shadow>\n </value>\n </block>\n </category>\n <category name="Lists" categorystyle="list_category">\n <block type="lists_create_with">\n <mutation items="0"></mutation>\n </block>\n <block type="lists_create_with"></block>\n <block type="lists_repeat">\n <value name="NUM">\n <shadow type="math_number">\n <field name="NUM">5</field>\n </shadow>\n </value>\n </block>\n <block type="lists_length"></block>\n <block type="lists_isEmpty"></block>\n <block type="lists_indexOf">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_getIndex">\n <value name="VALUE">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_setIndex">\n <value name="LIST">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_getSublist">\n <value name="LIST">\n <block type="variables_get">\n <field name="VAR">list</field>\n </block>\n </value>\n </block>\n <block type="lists_split">\n <value name="DELIM">\n <shadow type="text">\n <field name="TEXT">,</field>\n </shadow>\n </value>\n </block>\n <block type="lists_sort"></block>\n <block type="lists_reverse"></block>\n </category>\n <category name="Variables" categorystyle="variable_category" custom="VARIABLE"></category>\n <category name="Functions" categorystyle="procedure_category" custom="PROCEDURE"></category>\n\n \x3c!-- END: Built-in Blockly Blocks //--\x3e\n\n </xml>\n';
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { InputTextProps } from 'primereact/inputtext';
|
|
2
3
|
import { EventEmitterContext } from "../core/EventEmitterContext.js";
|
|
3
4
|
import { KeyFilterType } from "primereact/keyfilter";
|
|
4
5
|
/**
|
|
5
6
|
* Properties of the component.
|
|
6
7
|
*/
|
|
7
|
-
interface TextInputProps {
|
|
8
|
+
interface TextInputProps extends Omit<InputTextProps, "prefix"> {
|
|
8
9
|
/**
|
|
9
10
|
* The label for the field.
|
|
10
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React from"react";import{InputText}from"primereact/inputtext";import{Button}from"primereact/button";import{EventEmitterContext}from"../core/EventEmitterContext.js";export class TextInput extends React.Component{constructor(t){super(t),Object.defineProperty(this,"validateValue",{enumerable:!0,configurable:!0,writable:!0,value:t=>void 0===this.props.validator||null===this.props.validator||this.props.validator.test(t)}),this.state={entryValue:t.value,isValid:void 0!==t.value&&this.validateValue(t.value),editing:!1}}componentDidMount(){}componentDidUpdate(t){t.value!==this.props.value&&this.setState({entryValue:this.props.value,editing:!1})}onAcceptValue(){void 0!==this.state.entryValue&&this.validateValue(this.state.entryValue)?(this.setState({isValid:!0,editing:!1}),this.props.onValueChanged&&this.props.onValueChanged(this.state.entryValue),void 0!==this.props.dispatchTopic&&this.context.dispatch({topic:this.props.dispatchTopic,payload:this.state.entryValue})):this.setState({isValid:!1})}onResetValue(){this.setState({entryValue:this.props.value,isValid:void 0!==this.props.value&&this.validateValue(this.props.value),editing:!1})}render(){return _jsxs("div",{children:[_jsxs("div",{className:"p-inputgroup flex-1",children:[_jsx("span",{className:"p-inputgroup-addon",children:this.props.label}),void 0!==this.props.prefix&&_jsx("span",{className:"p-inputgroup-addon",children:this.props.prefix}),_jsx(InputText,{keyfilter:this.props.keyFilter,placeholder:this.props.placeholder,value:this.state.entryValue,onChange:t=>{this.setState({entryValue:t.target.value,editing:!0})},className:this.state.isValid?"":"p-invalid",onKeyDown:t=>{"Enter"===t.key?this.onAcceptValue():"Escape"===t.key&&this.onResetValue()},disabled:this.props.disabled}),void 0!==this.props.suffix&&_jsx("span",{className:"p-inputgroup-addon",children:this.props.suffix}),_jsx(Button,{icon:"pi pi-check",disabled:this.props.disabled||!this.state.editing,className:"p-button-success",onClick:()=>this.onAcceptValue(),visible:this.state.editing}),_jsx(Button,{icon:"pi pi-times",disabled:this.props.disabled||!this.state.editing,className:"p-button-danger",onClickCapture:()=>this.onResetValue(),visible:this.state.editing})]}),void 0!==this.props.description&&_jsx("small",{children:this.props.description})]})}}Object.defineProperty(TextInput,"contextType",{enumerable:!0,configurable:!0,writable:!0,value:EventEmitterContext}),Object.defineProperty(TextInput,"defaultProps",{enumerable:!0,configurable:!0,writable:!0,value:{label:"",value:void 0,keyFilter:void 0,writeTopic:void 0,onValueChanged:void 0,description:void 0,prefix:void 0,suffix:void 0,disabled:!1,dispatchTopic:void 0,placeholder:void 0,validator:void 0}});
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React from"react";import{InputText}from"primereact/inputtext";import{Button}from"primereact/button";import{EventEmitterContext}from"../core/EventEmitterContext.js";export class TextInput extends React.Component{constructor(t){super(t),Object.defineProperty(this,"validateValue",{enumerable:!0,configurable:!0,writable:!0,value:t=>void 0===this.props.validator||null===this.props.validator||this.props.validator.test(t)}),this.state={entryValue:t.value,isValid:void 0!==t.value&&this.validateValue(t.value),editing:!1}}componentDidMount(){}componentDidUpdate(t){t.value!==this.props.value&&this.setState({entryValue:this.props.value,editing:!1})}onAcceptValue(){void 0!==this.state.entryValue&&this.validateValue(this.state.entryValue)?(this.setState({isValid:!0,editing:!1}),this.props.onValueChanged&&this.props.onValueChanged(this.state.entryValue),void 0!==this.props.dispatchTopic&&this.context.dispatch({topic:this.props.dispatchTopic,payload:this.state.entryValue})):this.setState({isValid:!1})}onResetValue(){this.setState({entryValue:this.props.value,isValid:void 0!==this.props.value&&this.validateValue(this.props.value),editing:!1})}render(){const{prefix:t,...e}=this.props;return _jsxs("div",{children:[_jsxs("div",{className:"p-inputgroup flex-1",children:[_jsx("span",{className:"p-inputgroup-addon",children:this.props.label}),void 0!==this.props.prefix&&_jsx("span",{className:"p-inputgroup-addon",children:this.props.prefix}),_jsx(InputText,{...e,keyfilter:this.props.keyFilter,placeholder:this.props.placeholder,value:this.state.entryValue,onChange:t=>{this.setState({entryValue:t.target.value,editing:!0})},className:this.state.isValid?"":"p-invalid",onKeyDown:t=>{"Enter"===t.key?this.onAcceptValue():"Escape"===t.key&&this.onResetValue()},disabled:this.props.disabled}),void 0!==this.props.suffix&&_jsx("span",{className:"p-inputgroup-addon",children:this.props.suffix}),_jsx(Button,{icon:"pi pi-check",disabled:this.props.disabled||!this.state.editing,className:"p-button-success",onClick:()=>this.onAcceptValue(),visible:this.state.editing}),_jsx(Button,{icon:"pi pi-times",disabled:this.props.disabled||!this.state.editing,className:"p-button-danger",onClickCapture:()=>this.onResetValue(),visible:this.state.editing})]}),void 0!==this.props.description&&_jsx("small",{children:this.props.description})]})}}Object.defineProperty(TextInput,"contextType",{enumerable:!0,configurable:!0,writable:!0,value:EventEmitterContext}),Object.defineProperty(TextInput,"defaultProps",{enumerable:!0,configurable:!0,writable:!0,value:{label:"",value:void 0,keyFilter:void 0,writeTopic:void 0,onValueChanged:void 0,description:void 0,prefix:void 0,suffix:void 0,disabled:!1,dispatchTopic:void 0,placeholder:void 0,validator:void 0}});
|
|
@@ -47,10 +47,11 @@
|
|
|
47
47
|
* to the console when accepted.
|
|
48
48
|
*/
|
|
49
49
|
import React from 'react';
|
|
50
|
+
import { InputNumberProps } from 'primereact/inputnumber';
|
|
50
51
|
/**
|
|
51
52
|
* Properties of the ValueInput component.
|
|
52
53
|
*/
|
|
53
|
-
interface ValueInputProps {
|
|
54
|
+
interface ValueInputProps extends Omit<InputNumberProps, 'value'> {
|
|
54
55
|
/**
|
|
55
56
|
* The label for the ValueInput field.
|
|
56
57
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useState,useRef,useEffect,useContext}from"react";import{InputNumber}from"primereact/inputnumber";import{EventEmitterContext}from"../core/EventEmitterContext";import{Button}from"primereact/button";export const ValueInput=({label:e="",value:t=null,min:s,max:i,minPrecision:n=0,maxPrecision:o=3,mode:a="decimal",currency:u="USD",prefix:l,suffix:r,showButtons:c=!1,step:p=1,locale:m="en-US",description:d,disabled:x=!1,dispatchTopic:f,placeholder:v,onValueChanged:b})=>{const[
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useState,useRef,useEffect,useContext}from"react";import{InputNumber}from"primereact/inputnumber";import{EventEmitterContext}from"../core/EventEmitterContext";import{Button}from"primereact/button";export const ValueInput=({label:e="",value:t=null,min:s,max:i,minPrecision:n=0,maxPrecision:o=3,mode:a="decimal",currency:u="USD",prefix:l,suffix:r,showButtons:c=!1,step:p=1,locale:m="en-US",description:d,disabled:x=!1,dispatchTopic:f,placeholder:v,onValueChanged:b,...h})=>{const[j,C]=useState(t),[E,_]=useState(t),[S,g]=useState(t),[y,N]=useState(!1),[k,B]=useState(!1),D=useRef(null),F=useContext(EventEmitterContext);useEffect((()=>{null!==S?(_(S),g(null),B(!1)):t!==E&&(_(t),C(t),N(!1),B(!1))}),[t,E]);const I=()=>{var e;y&&null!==j&&(e=j,void 0!==i&&e>i||void 0!==s&&e<s?B(!0):(_(j),N(!1),b?.(j),B(!1),f&&F.dispatch({topic:f,payload:j})))},w=()=>{y&&(C(null),_(null),N(!1),B(!1))};return _jsxs("div",{children:[_jsxs("div",{className:"p-inputgroup flex-1",children:[_jsx("span",{className:"p-inputgroup-addon",children:e}),_jsx(InputNumber,{...h,ref:D,invalid:k,min:s,max:i,minFractionDigits:n,maxFractionDigits:o,mode:a,prefix:l,suffix:r,showButtons:c,step:p,placeholder:v,value:E,onChange:e=>{return t=e.value,y||(g(E),N(!0)),void C(t);var t},locale:m,currency:u,onKeyDown:e=>{"Enter"===e.key?I():"Escape"===e.key&&w()},disabled:x}),_jsx(Button,{icon:"pi pi-check",disabled:x||!y,className:"p-button-success",onClick:()=>I(),visible:y,size:"small",autoFocus:!1}),_jsx(Button,{icon:"pi pi-times",disabled:x||!y,className:"p-button-danger",onClickCapture:()=>w(),visible:y,size:"small",autoFocus:!1})]}),d&&_jsx("small",{children:d})]})};export default ValueInput;
|
package/dist/hooks/adsHooks.d.ts
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* and update the `speed` and `temperature` states via `setSpeed` and `setTemperature` respectively
|
|
30
30
|
* whenever new data is received.
|
|
31
31
|
*/
|
|
32
|
-
export declare const useAdsRegisterSymbols: (setters: Record<string, (value: any) => void>, symbols: Record<string, string>) => null;
|
|
32
|
+
export declare const useAdsRegisterSymbols: (setters: Record<string, (value: any) => void>, symbols: Record<string, string>, options?: Record<string, {}>) => null;
|
|
33
33
|
/**
|
|
34
34
|
* Custom hook to create a function that sends a specified value to a remote device or server.
|
|
35
35
|
* This hook abstracts the repetitive logic involved in sending these values,
|
package/dist/hooks/adsHooks.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useContext,useRef,useEffect,useCallback}from"react";import{EventEmitterContext}from"../core/EventEmitterContext";function sleep(e){return new Promise((t=>setTimeout(t,e)))}export const useAdsRegisterSymbols=(e,t)=>{const{invoke:
|
|
1
|
+
import{useContext,useRef,useEffect,useCallback}from"react";import{EventEmitterContext}from"../core/EventEmitterContext";function sleep(e){return new Promise((t=>setTimeout(t,e)))}export const useAdsRegisterSymbols=(e,t,n={})=>{const{invoke:r,subscribe:s,unsubscribe:o,isConnected:u}=useContext(EventEmitterContext),c=useRef([]),a=useRef(!0);return useEffect((()=>{a.current=!0;const i=async()=>{for(const[o,u]of Object.entries(t))try{const t=n[o]?{symbol_name:u,options:n[o]}:{symbol_name:u};await r("ADS","register_symbol",t);const i=s(`ADS/${u}`,(t=>{if(a.current){const n=e[o];n&&n(t.value)}}));c.current.push(i)}catch(e){}await r("ADS","refresh",{})};if(u())i();else{let e=s("HUB/connected",(()=>{i(),o(e)}));c.current.push(e)}return()=>{a.current=!1,c.current.forEach((e=>o(e))),c.current=[]}}),[]),null};export function useAdsWriteValue(e){const{invoke:t}=useContext(EventEmitterContext);return async n=>{try{await t("ADS","write_value",{symbol_name:e,value:n})}catch(e){}}}export function useAdsWriteScaledValue(e,t,n){const{invoke:r}=useContext(EventEmitterContext);return useCallback((async s=>{const o=(s-n)/t;try{await r("ADS","write_value",{symbol_name:e,value:o})}catch(e){}}),[e,t,n,r])}export function useAdsTapValue(e){const{invoke:t}=useContext(EventEmitterContext),n="write_value";return async()=>{try{await t("ADS",n,{symbol_name:e,value:!0}),await sleep(300),await t("ADS",n,{symbol_name:e,value:!1})}catch(e){}}}
|
package/package.json
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
3
3
|
* Created Date: 2024-03-15 13:21:06
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-05-20 08:38:24
|
|
6
6
|
* -----
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import React, { forwardRef, useImperativeHandle, useCallback, useState } from 'react';
|
|
10
11
|
import Blockly from "blockly";
|
|
11
12
|
import { pythonGenerator } from 'blockly/python';
|
|
12
13
|
|
|
13
14
|
import { BlocklyWorkspace } from 'react-blockly';
|
|
14
15
|
|
|
15
|
-
import React from 'react';
|
|
16
16
|
|
|
17
17
|
import "./BlocklyEditor.css";
|
|
18
18
|
|
|
@@ -25,7 +25,7 @@ interface BlocklyEditorProps {
|
|
|
25
25
|
* The initial XML representation of the Blockly workspace. Can be undefined
|
|
26
26
|
* if the workspace should start empty.
|
|
27
27
|
*/
|
|
28
|
-
initialXml
|
|
28
|
+
initialXml?: string | undefined;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* The configuration definition for the Blockly Toolbox.
|
|
@@ -52,7 +52,7 @@ interface BlocklyEditorProps {
|
|
|
52
52
|
* Callback fired when the Blockly workspace content changes.
|
|
53
53
|
* Note: This does not directly reflect changes to the XML or the code.
|
|
54
54
|
*/
|
|
55
|
-
onWorkspaceChanged?: () => void;
|
|
55
|
+
onWorkspaceChanged?: (workspace: Blockly.WorkspaceSvg | undefined) => void;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Callback fired when the XML representation of the workspace changes.
|
|
@@ -66,42 +66,12 @@ interface BlocklyEditorProps {
|
|
|
66
66
|
*/
|
|
67
67
|
onCodeChanged?: (code: string) => void;
|
|
68
68
|
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* State of the BlocklyEditor
|
|
74
|
-
*/
|
|
75
|
-
interface BlocklyEditorState {
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* A name for the Blockly editor, potentially used for identification.
|
|
79
|
-
*/
|
|
80
|
-
name: string;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The current XML representation of the workspace.
|
|
84
|
-
*/
|
|
85
|
-
xml: string;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* The current generated code (e.g., Python).
|
|
89
|
-
*/
|
|
90
|
-
code: string;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* The height of the first parent container with
|
|
94
|
-
* classname "blockly-container". It is important to set
|
|
95
|
-
* a parent container with this class name for auto-sizing to work!
|
|
96
|
-
*/
|
|
97
|
-
parentHeight: number;
|
|
98
69
|
|
|
99
70
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* a parent container with this class name for auto-sizing to work!
|
|
71
|
+
* Callback fired when an error occurs.
|
|
72
|
+
* Includes an error message.
|
|
103
73
|
*/
|
|
104
|
-
|
|
74
|
+
onError? : (message : string) => void;
|
|
105
75
|
|
|
106
76
|
}
|
|
107
77
|
|
|
@@ -118,62 +88,107 @@ interface BlocklyEditorState {
|
|
|
118
88
|
*
|
|
119
89
|
* Right now, the editor only supports generating python code, but a feature to specify the
|
|
120
90
|
* generator for different languages is planned.
|
|
91
|
+
*
|
|
92
|
+
* ## Usage Example
|
|
93
|
+
*
|
|
94
|
+
* ```tsx
|
|
95
|
+
* import React, { useRef } from 'react';
|
|
96
|
+
* import BlocklyEditor from './BlocklyEditor';
|
|
97
|
+
* import { StandardToolbox } from './toolbox';
|
|
98
|
+
*
|
|
99
|
+
* const App: React.FC = () => {
|
|
100
|
+
* const blocklyEditorRef = useRef<{ newBlockly: () => void; loadBlockly: (xmlString: string) => void }>(null);
|
|
101
|
+
*
|
|
102
|
+
* const handleNew = () => {
|
|
103
|
+
* if (blocklyEditorRef.current) {
|
|
104
|
+
* blocklyEditorRef.current.newBlockly();
|
|
105
|
+
* }
|
|
106
|
+
* };
|
|
107
|
+
*
|
|
108
|
+
* const handleLoad = (xmlString: string) => {
|
|
109
|
+
* if (blocklyEditorRef.current) {
|
|
110
|
+
* blocklyEditorRef.current.loadBlockly(xmlString);
|
|
111
|
+
* }
|
|
112
|
+
* };
|
|
113
|
+
*
|
|
114
|
+
* return (
|
|
115
|
+
* <div className="blockly-container" style={{ height: '500px' }}>
|
|
116
|
+
* <BlocklyEditor
|
|
117
|
+
* ref={blocklyEditorRef}
|
|
118
|
+
* initialXml="<xml xmlns='http://www.w3.org/1999/xhtml'></xml>"
|
|
119
|
+
* toolbox={StandardToolbox}
|
|
120
|
+
* widthAdjustment={0}
|
|
121
|
+
* heightAdjustment={0}
|
|
122
|
+
* onWorkspaceChanged={(workspace) => console.log('Workspace changed:', workspace)}
|
|
123
|
+
* onXmlChanged={(xml) => console.log('XML changed:', xml)}
|
|
124
|
+
* onCodeChanged={(code) => console.log('Code changed:', code)}
|
|
125
|
+
* onError={(message) => console.error('Error:', message)}
|
|
126
|
+
* />
|
|
127
|
+
* <button onClick={handleNew}>New</button>
|
|
128
|
+
* <button onClick={() => handleLoad('<xml xmlns="http://www.w3.org/1999/xhtml"><block type="controls_if" x="10" y="10"></block></xml>')}>Load</button>
|
|
129
|
+
* </div>
|
|
130
|
+
* );
|
|
131
|
+
* };
|
|
132
|
+
*
|
|
133
|
+
* export default App;
|
|
134
|
+
* ```
|
|
121
135
|
*/
|
|
122
|
-
export
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
export const BlocklyEditor = forwardRef((props: BlocklyEditorProps, ref) => {
|
|
137
|
+
|
|
138
|
+
const [_xml, setXml] = useState<string>("");
|
|
139
|
+
const [_code, setCode] = useState<string>("");
|
|
140
|
+
const [parentWidth, setParentWidth] = useState<number>(0);
|
|
141
|
+
const [parentHeight, setParentHeight] = useState<number>(0);
|
|
142
|
+
const [editorWorkspace, setEditorWorkspace] = useState<Blockly.WorkspaceSvg | undefined>(undefined);
|
|
143
|
+
|
|
144
|
+
const containerRef = React.createRef<HTMLDivElement>();
|
|
145
|
+
|
|
146
|
+
useImperativeHandle(ref, () => ({
|
|
147
|
+
/**
|
|
148
|
+
* Clears the Blockly workspace, removing all blocks.
|
|
149
|
+
*/
|
|
150
|
+
newBlockly() {
|
|
151
|
+
if (editorWorkspace) {
|
|
152
|
+
editorWorkspace.clear();
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
/**
|
|
156
|
+
* Loads the given XML string into the Blockly workspace.
|
|
157
|
+
* @param xmlString - The XML string representing the Blockly workspace to be loaded.
|
|
158
|
+
*/
|
|
159
|
+
loadBlockly(xmlString: string) {
|
|
160
|
+
if (editorWorkspace) {
|
|
161
|
+
try {
|
|
162
|
+
editorWorkspace.clear();
|
|
163
|
+
const xml = Blockly.utils.xml.textToDom(xmlString);
|
|
164
|
+
Blockly.Xml.domToWorkspace(xml, editorWorkspace);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
if (props.onError) {
|
|
167
|
+
props.onError(`${err}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}));
|
|
135
173
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
this.state = {
|
|
142
|
-
name: '',
|
|
143
|
-
xml: '',
|
|
144
|
-
code: '',
|
|
145
|
-
parentWidth: 0,
|
|
146
|
-
parentHeight: 0
|
|
174
|
+
React.useEffect(() => {
|
|
175
|
+
updateParentSize();
|
|
176
|
+
window.addEventListener('resize', updateParentSize);
|
|
177
|
+
return () => {
|
|
178
|
+
window.removeEventListener('resize', updateParentSize);
|
|
147
179
|
};
|
|
180
|
+
}, []);
|
|
148
181
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
componentDidUpdate(prevProps: BlocklyEditorProps) {
|
|
157
|
-
prevProps;
|
|
158
|
-
// Logic to handle updates in props, if necessary
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
componentWillUnmount() {
|
|
162
|
-
window.removeEventListener('resize', this.updateParentSize);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
updateParentSize = () => {
|
|
167
|
-
|
|
168
|
-
if (this.ref.current) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const parent = this.ref.current.parentElement;
|
|
182
|
+
/**
|
|
183
|
+
* Updates the size of the parent container to ensure the Blockly workspace fits within it.
|
|
184
|
+
*/
|
|
185
|
+
const updateParentSize = useCallback(() => {
|
|
186
|
+
if (containerRef.current) {
|
|
172
187
|
let w = 0;
|
|
173
188
|
let h = 0;
|
|
174
|
-
let p =
|
|
189
|
+
let p = containerRef.current.parentElement;
|
|
175
190
|
|
|
176
|
-
while (p !==
|
|
191
|
+
while (p !== null) {
|
|
177
192
|
w = p.offsetWidth;
|
|
178
193
|
h = p.offsetHeight;
|
|
179
194
|
|
|
@@ -184,72 +199,68 @@ export class BlocklyEditor extends React.Component<BlocklyEditorProps, BlocklyEd
|
|
|
184
199
|
p = p.parentElement;
|
|
185
200
|
}
|
|
186
201
|
|
|
187
|
-
if (w < 300)
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (h < 300)
|
|
192
|
-
h = 300;
|
|
202
|
+
if (w < 300) w = 300;
|
|
203
|
+
if (h < 300) h = 300;
|
|
193
204
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
parentHeight: (h * 0.9) + this.props.heightAdjustment,
|
|
197
|
-
});
|
|
205
|
+
setParentWidth((w * 0.9) + props.widthAdjustment);
|
|
206
|
+
setParentHeight((h * 0.9) + props.heightAdjustment);
|
|
198
207
|
}
|
|
199
|
-
};
|
|
200
|
-
|
|
208
|
+
}, [props.widthAdjustment, props.heightAdjustment]);
|
|
201
209
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Callback function that is triggered when the Blockly workspace changes.
|
|
212
|
+
* It updates the generated code and triggers the appropriate callback.
|
|
213
|
+
* @param workspace - The Blockly workspace that has changed.
|
|
214
|
+
*/
|
|
215
|
+
const onWorkspaceChanged = (workspace: Blockly.WorkspaceSvg | undefined) => {
|
|
205
216
|
const code = pythonGenerator.workspaceToCode(workspace);
|
|
206
|
-
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (this.props.onCodeChanged !== undefined && this.props.onCodeChanged !== null)
|
|
211
|
-
this.props.onCodeChanged(code);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private setXml = (xml: string) => {
|
|
215
|
-
this.setState({ xml: xml });
|
|
216
|
-
|
|
217
|
-
if (this.props.onXmlChanged !== undefined && this.props.onXmlChanged !== null)
|
|
218
|
-
this.props.onXmlChanged(xml);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private onInject = (workspace: Blockly.WorkspaceSvg) => {
|
|
222
|
-
workspace;
|
|
223
|
-
}
|
|
217
|
+
setCode(code);
|
|
218
|
+
if (props.onWorkspaceChanged) props.onWorkspaceChanged(workspace);
|
|
219
|
+
if (props.onCodeChanged) props.onCodeChanged(code);
|
|
220
|
+
};
|
|
224
221
|
|
|
225
222
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
onInject={this.onInject}
|
|
236
|
-
workspaceConfiguration={{
|
|
237
|
-
grid: {
|
|
238
|
-
spacing: 20,
|
|
239
|
-
length: 3,
|
|
240
|
-
colour: "#ccc",
|
|
241
|
-
snap: true,
|
|
242
|
-
},
|
|
243
|
-
}}
|
|
244
|
-
/>
|
|
245
|
-
|
|
246
|
-
</>
|
|
247
|
-
|
|
248
|
-
)
|
|
223
|
+
/**
|
|
224
|
+
* Callback function that is triggered when the XML representation of the workspace changes.
|
|
225
|
+
* @param s - The updated XML string.
|
|
226
|
+
*/
|
|
227
|
+
const onXmlChanged = (s : string) => {
|
|
228
|
+
setXml(s);
|
|
229
|
+
if (props.onXmlChanged !== undefined && props.onXmlChanged !== null) {
|
|
230
|
+
props.onXmlChanged(s);
|
|
231
|
+
}
|
|
249
232
|
}
|
|
250
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Callback function that is triggered when the Blockly workspace is injected.
|
|
236
|
+
* It sets the workspace reference.
|
|
237
|
+
* @param workspace - The Blockly workspace that has been injected.
|
|
238
|
+
*/
|
|
239
|
+
const onInject = (workspace: Blockly.WorkspaceSvg) => {
|
|
240
|
+
setEditorWorkspace(workspace);
|
|
241
|
+
};
|
|
251
242
|
|
|
252
|
-
|
|
243
|
+
return (
|
|
244
|
+
<div ref={containerRef} style={{ width: parentWidth, height: parentHeight }}>
|
|
245
|
+
<BlocklyWorkspace
|
|
246
|
+
className="fill-height"
|
|
247
|
+
toolboxConfiguration={props.toolbox}
|
|
248
|
+
initialXml={props.initialXml}
|
|
249
|
+
onWorkspaceChange={onWorkspaceChanged}
|
|
250
|
+
onXmlChange={onXmlChanged}
|
|
251
|
+
onInject={onInject}
|
|
252
|
+
workspaceConfiguration={{
|
|
253
|
+
grid: {
|
|
254
|
+
spacing: 20,
|
|
255
|
+
length: 3,
|
|
256
|
+
colour: "#ccc",
|
|
257
|
+
snap: true,
|
|
258
|
+
},
|
|
259
|
+
}}
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
});
|
|
253
264
|
|
|
254
265
|
export default BlocklyEditor;
|
|
255
266
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
3
3
|
* Created Date: 2024-03-20 13:05:42
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-05-17 10:04:53
|
|
6
6
|
* -----
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import React from "react";
|
|
12
12
|
|
|
13
|
-
import { InputText } from 'primereact/inputtext';
|
|
13
|
+
import { InputText, InputTextProps } from 'primereact/inputtext';
|
|
14
14
|
import { Button } from "primereact/button";
|
|
15
15
|
|
|
16
16
|
import { EventEmitterContext } from "../core/EventEmitterContext.js";
|
|
@@ -19,7 +19,7 @@ import { KeyFilterType } from "primereact/keyfilter";
|
|
|
19
19
|
/**
|
|
20
20
|
* Properties of the component.
|
|
21
21
|
*/
|
|
22
|
-
interface TextInputProps {
|
|
22
|
+
interface TextInputProps extends Omit<InputTextProps, "prefix"> {
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* The label for the field.
|
|
@@ -211,6 +211,8 @@ export class TextInput extends React.Component<TextInputProps, TextInputState> {
|
|
|
211
211
|
|
|
212
212
|
render() {
|
|
213
213
|
|
|
214
|
+
const { prefix, ...restProps} = this.props;
|
|
215
|
+
|
|
214
216
|
return(
|
|
215
217
|
<div>
|
|
216
218
|
<div className="p-inputgroup flex-1" >
|
|
@@ -223,6 +225,7 @@ export class TextInput extends React.Component<TextInputProps, TextInputState> {
|
|
|
223
225
|
</span>
|
|
224
226
|
}
|
|
225
227
|
<InputText
|
|
228
|
+
{...restProps}
|
|
226
229
|
keyfilter={this.props.keyFilter}
|
|
227
230
|
placeholder={this.props.placeholder}
|
|
228
231
|
value={this.state.entryValue}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
3
3
|
* Created Date: 2024-03-20 13:05:42
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-05-17 10:01:45
|
|
6
6
|
* -----
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
import React, { useState, useRef, useEffect, useContext } from 'react';
|
|
61
|
-
import { InputNumber } from 'primereact/inputnumber';
|
|
61
|
+
import { InputNumber, InputNumberProps } from 'primereact/inputnumber';
|
|
62
62
|
import { EventEmitterContext } from "../core/EventEmitterContext";
|
|
63
63
|
import { Button } from 'primereact/button';
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Properties of the ValueInput component.
|
|
67
67
|
*/
|
|
68
|
-
interface ValueInputProps {
|
|
68
|
+
interface ValueInputProps extends Omit<InputNumberProps, 'value'> {
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* The label for the ValueInput field.
|
|
@@ -209,7 +209,8 @@ export const ValueInput: React.FC<ValueInputProps> = ({
|
|
|
209
209
|
disabled = false,
|
|
210
210
|
dispatchTopic = undefined,
|
|
211
211
|
placeholder = undefined,
|
|
212
|
-
onValueChanged = undefined
|
|
212
|
+
onValueChanged = undefined,
|
|
213
|
+
...restProps
|
|
213
214
|
}) => {
|
|
214
215
|
const [entryValue, setEntryValue] = useState<number | null>(value);
|
|
215
216
|
const [currentValue, setCurrentValue] = useState<number | null>(value);
|
|
@@ -311,6 +312,7 @@ export const ValueInput: React.FC<ValueInputProps> = ({
|
|
|
311
312
|
<div className="p-inputgroup flex-1">
|
|
312
313
|
<span className="p-inputgroup-addon">{label}</span>
|
|
313
314
|
<InputNumber
|
|
315
|
+
{...restProps}
|
|
314
316
|
ref={inputRef}
|
|
315
317
|
invalid={invalidValue}
|
|
316
318
|
min={min}
|
package/src/hooks/adsHooks.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
3
3
|
* Created Date: 2024-04-26 09:04:40
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-05-17 10:47:22
|
|
6
6
|
* -----
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
@@ -55,7 +55,11 @@ function sleep(ms: number): Promise<void> {
|
|
|
55
55
|
* and update the `speed` and `temperature` states via `setSpeed` and `setTemperature` respectively
|
|
56
56
|
* whenever new data is received.
|
|
57
57
|
*/
|
|
58
|
-
export const useAdsRegisterSymbols = (
|
|
58
|
+
export const useAdsRegisterSymbols = (
|
|
59
|
+
setters: Record<string, (value: any) => void>,
|
|
60
|
+
symbols: Record<string, string>,
|
|
61
|
+
options: Record<string, {}> = {},
|
|
62
|
+
) => {
|
|
59
63
|
const { invoke, subscribe, unsubscribe, isConnected } = useContext(EventEmitterContext);
|
|
60
64
|
const subscriptions = useRef<number[]>([]);
|
|
61
65
|
const isMounted = useRef(true);
|
|
@@ -66,7 +70,9 @@ export const useAdsRegisterSymbols = (setters: Record<string, (value: any) => vo
|
|
|
66
70
|
const registerAndSubscribe = async () => {
|
|
67
71
|
for (const [key, symbolName] of Object.entries(symbols)) {
|
|
68
72
|
try {
|
|
69
|
-
|
|
73
|
+
const invokeOptions = options[key] ? { symbol_name: symbolName, options : options[key] } : { symbol_name: symbolName };
|
|
74
|
+
await invoke("ADS", "register_symbol", invokeOptions);
|
|
75
|
+
|
|
70
76
|
//console.log(`Subscribe... ADS/${symbolName}`);
|
|
71
77
|
const subscriptionId = subscribe(`ADS/${symbolName}`, (data) => {
|
|
72
78
|
if (isMounted.current) {
|