@adcops/autocore-react 3.0.23 → 3.0.26

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.
@@ -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: string | undefined;
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,36 +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
- * The current generated code (e.g., Python).
45
+ * Callback fired when an error occurs.
46
+ * Includes an error message.
59
47
  */
60
- code: string;
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;
73
- initialXmlLoaded: boolean;
48
+ onError?: (message: string) => void;
74
49
  }
75
50
  /**
76
51
  * A view that contains the blockly editor, wrapping the react-blockly BlocklyWorkspace
@@ -84,30 +59,52 @@ interface BlocklyEditorState {
84
59
  *
85
60
  * Right now, the editor only supports generating python code, but a feature to specify the
86
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
+ * ```
87
106
  */
88
- export declare class BlocklyEditor extends React.Component<BlocklyEditorProps, BlocklyEditorState> {
89
- static defaultProps: {
90
- initialXml: undefined;
91
- widthAdjustment: number;
92
- heightAdjustment: number;
93
- onWorkspaceChanged: undefined;
94
- onXmlChanged: undefined;
95
- onCodeChanged: undefined;
96
- };
97
- private ref;
98
- /**
99
- * Constructor
100
- */
101
- constructor(props: BlocklyEditorProps);
102
- componentDidMount(): void;
103
- componentDidUpdate(prevProps: BlocklyEditorProps): void;
104
- componentWillUnmount(): void;
105
- updateParentSize: () => void;
106
- private onWorkspaceChanged;
107
- private setXml;
108
- private onInject;
109
- render(): import("react/jsx-runtime").JSX.Element;
110
- }
107
+ export declare const BlocklyEditor: React.ForwardRefExoticComponent<BlocklyEditorProps & React.RefAttributes<unknown>>;
111
108
  export default BlocklyEditor;
112
109
  /**
113
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.props.onXmlChanged&&this.props.onXmlChanged(e),this.setState({xml:e,initialXmlLoaded:!0})}}),Object.defineProperty(this,"onInject",{enumerable:!0,configurable:!0,writable:!0,value:e=>{}}),this.state={name:"",xml:"",code:"",parentWidth:0,parentHeight:0,initialXmlLoaded:!1}}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 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useState,useContext,useEffect}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Toolbar}from"primereact/toolbar";import{Button}from"primereact/button";import{ConfirmPopup,confirmPopup}from"primereact/confirmpopup";import{FileUpload}from"primereact/fileupload";import{EventEmitterContext}from"../core/EventEmitterContext";export const FileList=({domain:e="DATASTORE",enableUpload:t=!1,subdir:o,filter:a=".json",onSuccess:n,onError:i})=>{const[r,l]=useState(0),{invoke:s}=useContext(EventEmitterContext),[c,m]=useState(),d=e=>null!==e?void 0!==o?`${o}/${e}`:e:"",p=async()=>{try{const t=void 0!==o?{subdir:o}:{};let a=await s(e,"list_files",t),n=[];for(let e=0;e<a.data.length;++e){const t=a.data[e];n.push({id:e+1,name:t})}m(n)}catch(e){i&&i(`Failed to upload file list: ${e}`)}};const u=()=>{l((e=>e+1))},f=`File Listing [/${void 0!==o?o:""}]`,x=_jsx(React.Fragment,{children:_jsx("span",{style:{fontWeight:600},children:f})}),b=_jsxs(React.Fragment,{children:[t&&_jsx(FileUpload,{customUpload:!0,auto:!0,uploadHandler:async t=>{const o=t.files[0];let a=d(o.name);const r=new FileReader;r.onload=async t=>{const r=t.target?.result,l=function c(e){let t="",o=new Uint8Array(e),a=o.byteLength;for(let e=0;e<a;e++)t+=String.fromCharCode(o[e]);return window.btoa(t)}(r);try{await s(e,"write_file",{file_name:a,value:l}),n&&n(`Uploaded file ${o.name}`),p()}catch(e){i&&i(`Failed to upload file: ${e}`)}u()},r.onerror=e=>{i&&i(`Error reading file: ${e}`)},r.readAsArrayBuffer(o)},accept:a,maxFileSize:25e3,mode:"basic",chooseLabel:"",chooseOptions:{icon:"pi pi-upload",className:"p-button-icon-only p-button-text p-button-rounded p-mr-2"}},r),_jsx(Button,{icon:"pi pi-refresh",onClick:()=>{p()},className:"p-button-rounded p-mr-2","aria-label":"Refresh",size:"small",rounded:!0,text:!0})]}),_=(t,o)=>{confirmPopup({target:o.currentTarget,message:`Are you want to delete file ${t.name}?\nWARNING: This cannot be undone.`,icon:"pi pi-info-circle",defaultFocus:"reject",acceptClassName:"p-button-danger",accept:()=>(async t=>{let o=d(t);try{await s(e,"delete_file",{file_name:o}),n&&n(`Deleted file: ${t}`),p()}catch(e){i&&i(`Failed to delete file: ${e}`)}p()})(t.name)})};return useEffect((()=>(p(),()=>{})),[e,t]),_jsxs("div",{children:[_jsx(Toolbar,{start:x,end:b,style:{padding:"1mm"}}),_jsx(ConfirmPopup,{}),_jsxs(DataTable,{value:c,children:[_jsx(Column,{field:"name",header:"Name"}),_jsx(Column,{body:t=>_jsxs(_Fragment,{children:[_jsx(Button,{icon:"pi pi-download",onClick:()=>(async t=>{let o=d(t.name);try{await s(e,"download_file",{file_name:o}),n&&n(`Downloaded file: ${t.name}`)}catch(e){i&&i(`Failed downloading file: ${e}`)}})(t),className:"p-button-rounded p-button-success p-mr-2",style:{marginRight:"2mm"},size:"small"}),_jsx(Button,{icon:"pi pi-trash",onClick:e=>_(t,e),className:"p-button-rounded p-button-danger",size:"small"})]}),header:"Actions"})]})]})};export default FileList;
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useState,useContext,useEffect}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Toolbar}from"primereact/toolbar";import{Button}from"primereact/button";import{ConfirmPopup,confirmPopup}from"primereact/confirmpopup";import{FileUpload}from"primereact/fileupload";import{EventEmitterContext}from"../core/EventEmitterContext";export const FileList=({domain:e="DATASTORE",enableUpload:t=!1,subdir:o,filter:a=".json",onSuccess:n,onError:i})=>{const[r,l]=useState(0),{invoke:s}=useContext(EventEmitterContext),[c,m]=useState(),d=e=>null!==e?void 0!==o?`${o}/${e}`:e:"",p=async()=>{try{const t=void 0!==o?{subdir:o}:{};let a=await s(e,"list_files",t),n=[];for(let e=0;e<a.data.length;++e){const t=a.data[e];n.push({id:e+1,name:t})}m(n)}catch(e){i&&i(`Failed to upload file list: ${e}`)}};const u=()=>{l((e=>e+1))},f=`File Listing [/${void 0!==o?o:""}]`,x=_jsx(React.Fragment,{children:_jsx("span",{style:{fontWeight:600},children:f})}),b=_jsxs(React.Fragment,{children:[t&&_jsx(FileUpload,{customUpload:!0,auto:!0,uploadHandler:async t=>{const o=t.files[0];let a=d(o.name);const r=new FileReader;r.onload=async t=>{const r=t.target?.result,l=function c(e){let t="",o=new Uint8Array(e),a=o.byteLength;for(let e=0;e<a;e++)t+=String.fromCharCode(o[e]);return window.btoa(t)}(r);try{await s(e,"write_file",{file_name:a,value:l,options:{base64:!0}}),n&&n(`Uploaded file ${o.name}`),p()}catch(e){i&&i(`Failed to upload file: ${e}`)}u()},r.onerror=e=>{i&&i(`Error reading file: ${e}`)},r.readAsArrayBuffer(o)},accept:a,maxFileSize:25e3,mode:"basic",chooseLabel:"",chooseOptions:{icon:"pi pi-upload",className:"p-button-icon-only p-button-text p-button-rounded p-mr-2"}},r),_jsx(Button,{icon:"pi pi-refresh",onClick:()=>{p()},className:"p-button-rounded p-mr-2","aria-label":"Refresh",size:"small",rounded:!0,text:!0})]}),_=(t,o)=>{confirmPopup({target:o.currentTarget,message:`Are you want to delete file ${t.name}?\nWARNING: This cannot be undone.`,icon:"pi pi-info-circle",defaultFocus:"reject",acceptClassName:"p-button-danger",accept:()=>(async t=>{let o=d(t);try{await s(e,"delete_file",{file_name:o}),n&&n(`Deleted file: ${t}`),p()}catch(e){i&&i(`Failed to delete file: ${e}`)}p()})(t.name)})};return useEffect((()=>(p(),()=>{})),[e,t]),_jsxs("div",{children:[_jsx(Toolbar,{start:x,end:b,style:{padding:"1mm"}}),_jsx(ConfirmPopup,{}),_jsxs(DataTable,{value:c,children:[_jsx(Column,{field:"name",header:"Name"}),_jsx(Column,{body:t=>_jsxs(_Fragment,{children:[_jsx(Button,{icon:"pi pi-download",onClick:()=>(async t=>{let o=d(t.name);try{await s(e,"download_file",{file_name:o}),n&&n(`Downloaded file: ${t.name}`)}catch(e){i&&i(`Failed downloading file: ${e}`)}})(t),className:"p-button-rounded p-button-success p-mr-2",style:{marginRight:"2mm"},size:"small"}),_jsx(Button,{icon:"pi pi-trash",onClick:e=>_(t,e),className:"p-button-rounded p-button-danger",size:"small"})]}),header:"Actions"})]})]})};export default FileList;
@@ -1,4 +1,11 @@
1
+ /**
2
+ * Multiple millimeters by this constant for inches.
3
+ */
1
4
  export declare const kMillimeters2Inches: number;
5
+ /**
6
+ * Multiply netwons by this constant for pounds.
7
+ */
8
+ export declare const kNewtons2Pounds: number;
2
9
  type UseScaledValueReturn = [number, (newValue: number) => void];
3
10
  /**
4
11
  * A custom React hook for converting values between different scales,
@@ -1 +1 @@
1
- import{useCallback,useState,useEffect}from"react";export const kMillimeters2Inches=1/25.4;export function useScaledValue(e,t,s=0){const[u,a]=useState(e),[c,l]=useState((()=>e*t+s));useEffect((()=>{l(u*t+s)}),[u,t,s]);return[c,useCallback((e=>{a(e)}),[t,s])]}
1
+ import{useCallback,useState,useEffect}from"react";export const kMillimeters2Inches=1/25.4;export const kNewtons2Pounds=1/4.4482216153;export function useScaledValue(e,t,s=0){const[u,c]=useState(e),[o,a]=useState((()=>e*t+s));useEffect((()=>{a(u*t+s)}),[u,t,s]);return[o,useCallback((e=>{c(e)}),[t,s])]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcops/autocore-react",
3
- "version": "3.0.23",
3
+ "version": "3.0.26",
4
4
  "description": "A React component library for industrial user interfaces.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -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-05-17 11:36:58
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: string | undefined;
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,45 +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
- * The width of the first parent container with
101
- * classname "blockly-container". It is important to set
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
- parentWidth: number;
105
-
106
-
107
- initialXmlLoaded : boolean;
74
+ onError? : (message : string) => void;
108
75
 
109
76
  }
110
77
 
@@ -121,63 +88,107 @@ interface BlocklyEditorState {
121
88
  *
122
89
  * Right now, the editor only supports generating python code, but a feature to specify the
123
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
+ * ```
124
135
  */
125
- export class BlocklyEditor extends React.Component<BlocklyEditorProps, BlocklyEditorState> {
126
-
127
- static defaultProps = {
128
- initialXml: undefined,
129
- widthAdjustment: 0,
130
- heightAdjustment: 0,
131
- onWorkspaceChanged: undefined,
132
- onXmlChanged : undefined,
133
- onCodeChanged : undefined
134
- };
135
-
136
- // Explicitly type the ref as a reference to a HTMLDivElement
137
- private ref = React.createRef<HTMLDivElement>();
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
+ }));
138
173
 
139
- /**
140
- * Constructor
141
- */
142
- constructor(props: BlocklyEditorProps) {
143
- super(props);
144
- this.state = {
145
- name: '',
146
- xml: '',
147
- code: '',
148
- parentWidth: 0,
149
- parentHeight: 0,
150
- initialXmlLoaded : false
174
+ React.useEffect(() => {
175
+ updateParentSize();
176
+ window.addEventListener('resize', updateParentSize);
177
+ return () => {
178
+ window.removeEventListener('resize', updateParentSize);
151
179
  };
180
+ }, []);
152
181
 
153
- }
154
-
155
- componentDidMount() {
156
- this.updateParentSize();
157
- window.addEventListener('resize', this.updateParentSize);
158
- }
159
-
160
- componentDidUpdate(prevProps: BlocklyEditorProps) {
161
- prevProps;
162
- // Logic to handle updates in props, if necessary
163
- }
164
-
165
- componentWillUnmount() {
166
- window.removeEventListener('resize', this.updateParentSize);
167
- }
168
-
169
-
170
- updateParentSize = () => {
171
-
172
- if (this.ref.current) {
173
-
174
-
175
- 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) {
176
187
  let w = 0;
177
188
  let h = 0;
178
- let p = parent;
189
+ let p = containerRef.current.parentElement;
179
190
 
180
- while (p !== undefined && p !== null) {
191
+ while (p !== null) {
181
192
  w = p.offsetWidth;
182
193
  h = p.offsetHeight;
183
194
 
@@ -188,75 +199,68 @@ export class BlocklyEditor extends React.Component<BlocklyEditorProps, BlocklyEd
188
199
  p = p.parentElement;
189
200
  }
190
201
 
191
- if (w < 300) {
192
- w = 300;
193
- }
194
-
195
- if (h < 300)
196
- h = 300;
202
+ if (w < 300) w = 300;
203
+ if (h < 300) h = 300;
197
204
 
198
- this.setState({
199
- parentWidth: (w * 0.9) + this.props.widthAdjustment,
200
- parentHeight: (h * 0.9) + this.props.heightAdjustment,
201
- });
205
+ setParentWidth((w * 0.9) + props.widthAdjustment);
206
+ setParentHeight((h * 0.9) + props.heightAdjustment);
202
207
  }
203
- };
208
+ }, [props.widthAdjustment, props.heightAdjustment]);
204
209
 
205
-
206
-
207
-
208
- private onWorkspaceChanged = (workspace: Blockly.WorkspaceSvg | undefined) => {
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) => {
209
216
  const code = pythonGenerator.workspaceToCode(workspace);
210
- this.setState({ code: code });
211
- if (this.props.onWorkspaceChanged !== undefined && this.props.onWorkspaceChanged !== null)
212
- this.props.onWorkspaceChanged();
213
-
214
- if (this.props.onCodeChanged !== undefined && this.props.onCodeChanged !== null)
215
- this.props.onCodeChanged(code);
216
- }
217
+ setCode(code);
218
+ if (props.onWorkspaceChanged) props.onWorkspaceChanged(workspace);
219
+ if (props.onCodeChanged) props.onCodeChanged(code);
220
+ };
217
221
 
218
- private setXml = (xml: string) => {
219
222
 
220
- if (this.props.onXmlChanged) {
221
- this.props.onXmlChanged(xml);
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);
222
231
  }
223
-
224
- this.setState({ xml: xml, initialXmlLoaded: true });
225
- }
226
-
227
- private onInject = (workspace: Blockly.WorkspaceSvg) => {
228
- workspace;
229
- }
230
-
231
-
232
- render() {
233
-
234
- return (
235
- <>
236
- <BlocklyWorkspace
237
- className="fill-height" // you can use whatever classes are appropriate for your app's CSS
238
- toolboxConfiguration={this.props.toolbox} // this must be a JSON toolbox definition
239
- initialXml={this.props.initialXml}
240
- onWorkspaceChange={this.onWorkspaceChanged}
241
- onXmlChange={this.setXml}
242
- onInject={this.onInject}
243
- workspaceConfiguration={{
244
- grid: {
245
- spacing: 20,
246
- length: 3,
247
- colour: "#ccc",
248
- snap: true,
249
- },
250
- }}
251
- />
252
-
253
- </>
254
-
255
- )
256
232
  }
257
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
+ };
258
242
 
259
- }
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
+ });
260
264
 
261
265
  export default BlocklyEditor;
262
266
 
@@ -2,7 +2,7 @@
2
2
  * Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
3
3
  * Created Date: 2024-04-24 16:01:53
4
4
  * -----
5
- * Last Modified: 2024-05-02 11:18:50
5
+ * Last Modified: 2024-05-21 13:38:23
6
6
  * -----
7
7
  *
8
8
  */
@@ -247,7 +247,7 @@ export const FileList: React.FC<FileListProps> = ({
247
247
  const base64String = arrayBufferToBase64(arrayBuffer);
248
248
 
249
249
  try {
250
- await invoke(domain, "write_file", { file_name: target, value: base64String });
250
+ await invoke(domain, "write_file", { file_name: target, value: base64String, options :{"base64" : true} });
251
251
 
252
252
  if (onSuccess)
253
253
  onSuccess(`Uploaded file ${file.name}`);
@@ -2,7 +2,7 @@
2
2
  * Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
3
3
  * Created Date: 2024-04-30 11:41:59
4
4
  * -----
5
- * Last Modified: 2024-05-02 15:14:03
5
+ * Last Modified: 2024-05-21 19:58:23
6
6
  * -----
7
7
  *
8
8
  */
@@ -11,9 +11,16 @@
11
11
 
12
12
  import { useCallback, useState, useEffect } from 'react';
13
13
 
14
-
14
+ /**
15
+ * Multiple millimeters by this constant for inches.
16
+ */
15
17
  export const kMillimeters2Inches: number = 1 / 25.4;
16
18
 
19
+ /**
20
+ * Multiply netwons by this constant for pounds.
21
+ */
22
+ export const kNewtons2Pounds : number = 1 / 4.4482216153;
23
+
17
24
 
18
25
  type UseScaledValueReturn = [number, (newValue: number) => void];
19
26