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

Feature/json serialization #90

Merged
merged 3 commits into from
Mar 6, 2023
Merged
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
22 changes: 20 additions & 2 deletions src/BlocklyWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,77 @@ import {BlocklyWorkspaceProps} from "./BlocklyWorkspaceProps";

const propTypes = {
initialXml: PropTypes.string,
initialJson: 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,
onWorkspaceChange: PropTypes.func,
onImportXmlError: PropTypes.func,
onImportError: PropTypes.func,
onXmlChange: PropTypes.func,
onJsonChange: PropTypes.func,
onInject: PropTypes.func,
onDispose: PropTypes.func,
};

const defaultProps = {
initialXml: null,
initialJson: null,
toolboxConfiguration: null,
workspaceConfiguration: null,
className: null,
onWorkspaceChange: null,
onImportXmlError: null,
onImportError: null,
onXmlChange: null,
onJsonChange: null,
onInject: null,
onDispose: null,
};

function BlocklyWorkspace({
initialXml,
initialJson,
toolboxConfiguration,
workspaceConfiguration,
className,
onWorkspaceChange,
onXmlChange,
onJsonChange,
onImportXmlError,
onImportError,
onInject,
onDispose,
}: BlocklyWorkspaceProps) {
const editorDiv = React.useRef(null);
const { xml } = useBlocklyWorkspace({
const { xml, json } = useBlocklyWorkspace({
ref: editorDiv,
initialXml,
initialJson,
toolboxConfiguration,
workspaceConfiguration,
onWorkspaceChange,
onImportXmlError,
onImportError,
onInject,
onDispose,
});
const onXmlChangeRef = React.useRef(onXmlChange);
React.useEffect(() => {
onXmlChangeRef.current = onXmlChange;
}, [onXmlChange]);
const onJsonChangeRef = React.useRef(onJsonChange);
React.useEffect(() => {
onJsonChangeRef.current = onJsonChange;
}, [onJsonChange]);
React.useEffect(() => {
if (onXmlChangeRef.current && xml) {
onXmlChangeRef.current(xml);
}
}, [xml]);
if (onJsonChangeRef.current && json) {
onJsonChangeRef.current(json);
}
}, [xml, json]);

return <div className={className} ref={editorDiv} />;
}
Expand Down
4 changes: 4 additions & 0 deletions src/BlocklyWorkspaceProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ import { RefObject } from "react";

export interface CommonBlocklyProps {
initialXml: string;
initialJson: object;
toolboxConfiguration: Blockly.utils.toolbox.ToolboxDefinition;
workspaceConfiguration: Blockly.BlocklyOptions;
onWorkspaceChange: (workspace: WorkspaceSvg) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onImportXmlError?: (error: any) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onImportError?: (error: any) => void;
onInject?: (newWorkspace: WorkspaceSvg) => void;
onDispose?: (workspace: WorkspaceSvg) => void;
}
export interface BlocklyWorkspaceProps extends CommonBlocklyProps {
className?: string;
onXmlChange?: (xml: string) => void;
onJsonChange?: (worksapceJson: object) => void;
}
export interface UseBlocklyProps extends CommonBlocklyProps {
ref: RefObject<Element>;
Expand Down
15 changes: 13 additions & 2 deletions src/dev-index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const TestEditor = () => {
const [toolboxConfiguration, setToolboxConfiguration] =
React.useState<ToolboxInfo>(ConfigFiles.INITIAL_TOOLBOX_JSON);
const [generatedXml, setGeneratedXml] = useState("");
const [generatedJson, setGeneratedJson] = useState("");
const [generatedCode, setGeneratedCode] = useState("");

React.useEffect(() => {
Expand Down Expand Up @@ -66,7 +67,8 @@ const TestEditor = () => {
});
const newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(workspace));
setGeneratedXml(newXml);

const newJson = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
setGeneratedJson(newJson);
const code = javascriptGenerator.workspaceToCode(workspace);
setGeneratedCode(code);
}, []);
Expand All @@ -75,10 +77,16 @@ const TestEditor = () => {
setGeneratedXml(newXml);
}, []);

const onJsonChange = React.useCallback((newJson) => {
setGeneratedJson(JSON.stringify(newJson));
}, []);
const [ serialState, setSerialState ] = useState<"XML" | "JSON">("XML")
return (
<>
<div style={{ height: "600px", width: "800px" }}>
<button onClick={(e)=>setSerialState((e.target as HTMLElement).innerText == "XML" ? "XML" : "JSON")}>{serialState == "XML" ? "JSON" : "XML"} </button>
<BlocklyWorkspace
key={serialState}
toolboxConfiguration={toolboxConfiguration}
workspaceConfiguration={{
grid: {
Expand All @@ -88,13 +96,16 @@ const TestEditor = () => {
snap: true,
},
}}
initialXml={ConfigFiles.INITIAL_XML}
initialXml={serialState === "XML" ? ConfigFiles.INITIAL_XML : undefined}
initialJson={serialState === "JSON" ? ConfigFiles.INITIAL_JSON : undefined}
className="fill-height"
onWorkspaceChange={onWorkspaceChange}
onXmlChange={onXmlChange}
onJsonChange={onJsonChange}
/>
</div>
<pre>{generatedXml}</pre>
<p>{generatedJson}</p>
<textarea
style={{ height: "200px", width: "400px" }}
value={generatedCode}
Expand Down
5 changes: 4 additions & 1 deletion src/initContent/content.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const INITIAL_XML =
'<xml xmlns="http://www.w3.org/1999/xhtml"><block type="text" x="70" y="30"><field name="TEXT"></field></block></xml>';
'<xml xmlns="http://www.w3.org/1999/xhtml"><block type="text" x="70" y="30"><field name="TEXT">XML</field></block></xml>';

const INITIAL_JSON = {"blocks":{"languageVersion":0,"blocks":[{"type":"text","id":"Y|Ad[E=)p$+Lu41MXB!o","x":70,"y":30,"fields":{"TEXT":"JSON"}}]}}

const INITIAL_TOOLBOX_JSON = {
kind: "categoryToolbox",
Expand Down Expand Up @@ -1024,6 +1026,7 @@ const INITIAL_TOOLBOX_CATEGORIES = [

const ConfigFiles = {
INITIAL_XML,
INITIAL_JSON,
INITIAL_TOOLBOX_XML,
INITIAL_TOOLBOX_JSON,
INITIAL_TOOLBOX_CATEGORIES,
Expand Down
52 changes: 44 additions & 8 deletions src/useBlocklyWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,31 @@ function importFromXml(
xml: string,
workspace: Workspace,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onImportXmlError?: (error: any) => void
onImportError?: (error: any) => void
) {
try {
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
return true;
} catch (e) {
if (onImportXmlError) {
onImportXmlError(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 {
Blockly.serialization.workspaces.load(json, workspace);
return true;
} catch (e) {
if (onImportError) {
onImportError(e);
}
return false;
}
Expand All @@ -24,15 +41,22 @@ function importFromXml(
const useBlocklyWorkspace = ({
ref,
initialXml,
initialJson,
toolboxConfiguration,
workspaceConfiguration,
onWorkspaceChange,
onImportXmlError,
onImportError,
onInject,
onDispose,
}: UseBlocklyProps): { workspace: WorkspaceSvg | null; xml: string | null } => {
}: UseBlocklyProps): { workspace: WorkspaceSvg | null; xml: string | null, json: object | null } => {
// onImportError replaces onImportXmlError
// This is done for not breaking the signature until depreaction
onImportError = onImportError ?? onImportXmlError

const [workspace, setWorkspace] = React.useState<WorkspaceSvg | null>(null);
const [xml, setXml] = React.useState<string | null>(initialXml);
const [json, setJson] = React.useState<object | null>(initialJson);
const [didInitialImport, setDidInitialImport] = React.useState(false);
const [didHandleNewWorkspace, setDidHandleNewWorkspace] =
React.useState(false);
Expand Down Expand Up @@ -134,7 +158,8 @@ const useBlocklyWorkspace = ({
if (newXml === xml) {
return;
}

const newJson = Blockly.serialization.workspaces.save(workspace);
setJson(newJson);
setXml(newXml);
}, 200);

Expand All @@ -149,15 +174,26 @@ const useBlocklyWorkspace = ({
// Initial Xml Changes
React.useEffect(() => {
if (xml && workspace && !didInitialImport) {
const success = importFromXml(xml, workspace, onImportXmlError);
const success = importFromXml(xml, workspace, onImportError);
if (!success) {
setXml(null);
}
setDidInitialImport(true);
}
}, [xml, workspace, didInitialImport, onImportXmlError]);
else if (json && workspace && !didInitialImport) {
const success = importFromJson(json, workspace, onImportError);
if (!success) {
setJson(null);
}
const jsonToXml = Blockly.Xml.domToText(
Blockly.Xml.workspaceToDom(workspace)
)
setXml(jsonToXml);
setDidInitialImport(true);
}
}, [json, xml, workspace, didInitialImport, onImportError]);

return { workspace, xml };
return { workspace, xml, json };
};

export default useBlocklyWorkspace;