Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update blocklyworkspace from external events via new props #127

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,15 @@ function MyBlocklyHookEmbed() {
All properties are optional.

* `initialXml`: The XML of the program to initially load in the editor.
* `initialJson`: The JSON of the program to initially load in the editor.
* `updateXml`: A reference to XML of the program. If this value is changed (i.e. external to the BlocklyWorkspace component or useBlocklyWorkspace hook), the Blockly workspace will update to reflect the new XML.
* `updateJson`: A reference to JSON of the program. If this value is changed (i.e. external to the BlocklyWorkspace component or useBlocklyWorkspace hook), the Blockly workspace will update to reflect the new JSON.
* `workspaceConfiguration`: Any configuration options to be passed into `Blockly.inject` (except for `toolbox`, which is a separate prop).
* `toolboxConfiguration`: A JSON toolbox configuration (see [the Blockly documentation](https://developers.google.com/blockly/guides/configure/web/toolbox#json) for details on this format).
* `className`: The value for the `class` attribute to be used on the `<div>` elements generated by this component. Typically you'll need to use this to set the height of the Blockly editor, using either an explicit `height` style, flexboxes, or some other means.
* `onWorkspaceChange`: A function called every time the content of the workspace changes. It should take a single argument, which is the Blockly workspace object. (You can call methods such as Blockly.JavaScript.workspaceToCode on this object.)
* `onXmlChange`: A function called every time the content of the workspace, debounced to be called at most once every 200 milliseconds. This function should take a single argument, which is the new XML generated from the workspace.
* `onXmlChange`: A function called every time the content of the workspace changes, debounced to be called at most once every 200 milliseconds. This function should take a single argument, which is the new XML generated from the workspace.
* `onJsonChange`: A function called every time the content of the workspace changes, debounced to be called at most once every 200 milliseconds. This function should take a single argument, which is the new JSON generated from the workspace.
* `onImportXmlError`: A function called if `initialXml` can't be imported. This function takes a single argument, which is the error thrown during XML import.
* `onInject`: A function called after the Blockly workspace is injected. This function takes a single argument, which is the newly-injected Blockly workspace object. This is a good place to add Blockly plugins, if desired.
* `onDispose`: A function called after the Blockly workspace is disposed and removed from the page. This function takes a single argument, which is the just-disposed Blockly workspace object. Some Blockly plugins need to use this to dispose their own resources.
Expand Down
7 changes: 7 additions & 0 deletions src/BlocklyWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { BlocklyWorkspaceProps } from "./BlocklyWorkspaceProps";
const propTypes = {
initialXml: PropTypes.string,
initialJson: PropTypes.object,
updateXml: PropTypes.string,
updateJson: PropTypes.object,
toolboxConfiguration: PropTypes.object, // eslint-disable-line react/forbid-prop-types
workspaceConfiguration: PropTypes.object, // eslint-disable-line react/forbid-prop-types
className: PropTypes.string,
Expand All @@ -21,6 +23,8 @@ const propTypes = {
function BlocklyWorkspace({
initialXml,
initialJson,
updateXml,
updateJson,
toolboxConfiguration,
workspaceConfiguration,
className,
Expand All @@ -37,13 +41,16 @@ function BlocklyWorkspace({
ref: editorDiv,
initialXml,
initialJson,
updateXml,
updateJson,
toolboxConfiguration,
workspaceConfiguration,
onWorkspaceChange,
onImportXmlError,
onImportError,
onInject,
onDispose,

});
const onXmlChangeRef = React.useRef(onXmlChange);
React.useEffect(() => {
Expand Down
2 changes: 2 additions & 0 deletions src/BlocklyWorkspaceProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { RefObject } from "react";
export interface CommonBlocklyProps {
initialXml?: string;
initialJson?: object;
updateXml?: string;
updateJson?: object;
toolboxConfiguration?: Blockly.utils.toolbox.ToolboxDefinition;
workspaceConfiguration: Blockly.BlocklyOptions;
onWorkspaceChange?: (workspace: WorkspaceSvg) => void;
Expand Down
26 changes: 23 additions & 3 deletions src/dev-index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const TestEditor = () => {
const [generatedXml, setGeneratedXml] = useState("");
const [generatedJson, setGeneratedJson] = useState("");
const [generatedCode, setGeneratedCode] = useState("");
const [updateXml, setUpdateXml] = useState("");
const [updateJson, setUpdateJson] = useState({});

React.useEffect(() => {
window.setTimeout(() => {
Expand Down Expand Up @@ -85,7 +87,7 @@ const TestEditor = () => {
const [serialState, setSerialState] = useState<"XML" | "JSON">("XML");
return (
<>
<div style={{ height: "600px", width: "800px" }}>
<div style={{ height: "600px", width: "800px", marginBlockEnd: "2rem" }}>
<button
onClick={(e) =>
setSerialState(
Expand Down Expand Up @@ -116,10 +118,28 @@ const TestEditor = () => {
onWorkspaceChange={onWorkspaceChange}
onXmlChange={onXmlChange}
onJsonChange={onJsonChange}
updateXml={updateXml}
updateJson={updateJson}
/>
</div>
<pre>{generatedXml}</pre>
<p>{generatedJson}</p>
<textarea
style={{ height: "200px", width: "400px" }}
value={generatedXml}
onChange={(ev)=>{
console.log('onChange generated xml', ev)
console.log(ev.target.value)
setUpdateXml(ev.target.value)
}}
/>
<textarea
style={{ height: "200px", width: "400px" }}
value={generatedJson}
onChange={(ev)=>{
console.log('onChange generated json', ev)
console.log(ev.target.value)
setUpdateJson(JSON.parse(ev.target.value))
}}
/>
<textarea
style={{ height: "200px", width: "400px" }}
value={generatedCode}
Expand Down
47 changes: 45 additions & 2 deletions src/useBlocklyWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,32 @@ function importFromXml(
}
}

function repeatableImportFromXml(
xml: string,
workspace: Workspace,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onImportError?: (error: any) => void
) {
try {
workspace.clear();
Blockly.Xml.domToWorkspace(Blockly.utils.xml.textToDom(xml), workspace);
return true;
} catch (e) {
if (onImportError) {
onImportError(e);
}
return false;
}
}

function importFromJson(
json: object,
workspace: Workspace,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onImportError?: (error: any) => void
) {
try {
workspace.clear();
Blockly.serialization.workspaces.load(json, workspace);
return true;
} catch (e) {
Expand All @@ -44,6 +63,8 @@ const useBlocklyWorkspace = ({
ref,
initialXml,
initialJson,
updateXml,
updateJson,
toolboxConfiguration,
workspaceConfiguration,
onWorkspaceChange,
Expand All @@ -57,7 +78,7 @@ const useBlocklyWorkspace = ({
json: object | null;
} => {
// onImportError replaces onImportXmlError
// This is done for not breaking the signature until depreaction
// This is done for not breaking the signature until deprecation
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops, thanks for the spelling fix! ❤️

onImportError = onImportError ?? onImportXmlError;

const [workspace, setWorkspace] = React.useState<WorkspaceSvg | null>(null);
Expand Down Expand Up @@ -181,7 +202,7 @@ const useBlocklyWorkspace = ({
};
}, [workspace, xml]);

// Initial Xml Changes
// Initial Xml/Json Changes
React.useEffect(() => {
if (xml && workspace && !didInitialImport) {
const success = importFromXml(xml, workspace, onImportError);
Expand All @@ -202,6 +223,28 @@ const useBlocklyWorkspace = ({
}
}, [json, xml, workspace, didInitialImport, onImportError]);

// update xml/json
React.useEffect(() => {
console.log('update xml/json', updateXml, updateJson)

if (updateXml && workspace) {
const success = repeatableImportFromXml(updateXml, workspace, onImportError);
if (!success) {
setXml(null);
}
} else if (updateJson && workspace) {
const success = importFromJson(updateJson, workspace, onImportError);
if (!success) {
setJson(null);
}
}

}, [updateXml, updateJson, workspace, onImportError]);

React.useEffect(() => {

}, [updateJson, workspace, onImportError]);

return { workspace, xml, json };
};

Expand Down