blockly3d/src/components/scriptEditor/ScriptEditorView.tsx

85 lines
2.8 KiB
TypeScript

import { useEffect, useRef } from "react";
import { observer } from "mobx-react-lite";
import { runInAction } from "mobx";
import * as Blockly from "blockly";
import type { ObjectType } from "../../types";
import './ScriptEditorView.scss';
const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
kind: 'flyoutToolbox',
contents: [
{ kind: 'block', type: 'controls_if' },
{ kind: 'block', type: 'controls_repeat_ext' },
{ kind: 'block', type: 'logic_compare' },
{ kind: 'block', type: 'math_number' },
{ kind: 'block', type: 'math_arithmetic' },
{ kind: 'block', type: 'text' },
{ kind: 'block', type: 'text_print' },
],
};
type ScriptEditorViewProps = {
objectType: ObjectType;
}
export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorViewProps) {
const containerRef = useRef<HTMLDivElement>(null);
const workspaceRef = useRef<Blockly.WorkspaceSvg | null>(null);
const isLoadingRef = useRef(false);
useEffect(() => {
if (!containerRef.current) return;
const workspace = Blockly.inject(containerRef.current, { toolbox: TOOLBOX });
workspaceRef.current = workspace;
if (objectType.program) {
try {
isLoadingRef.current = true;
Blockly.serialization.workspaces.load(JSON.parse(objectType.program), workspace);
} catch {
// corrupt program, start fresh
} finally {
isLoadingRef.current = false;
}
}
const listener = (e: Blockly.Events.Abstract) => {
if (isLoadingRef.current || e.isUiEvent) return;
const serialized = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
runInAction(() => { objectType.program = serialized; });
};
workspace.addChangeListener(listener);
const resizeObserver = new ResizeObserver(() => Blockly.svgResize(workspace));
resizeObserver.observe(containerRef.current);
return () => {
resizeObserver.disconnect();
workspace.removeChangeListener(listener);
workspace.dispose();
workspaceRef.current = null;
};
}, []);
useEffect(() => {
const workspace = workspaceRef.current;
if (!workspace) return;
isLoadingRef.current = true;
workspace.clear();
if (objectType.program) {
try {
Blockly.serialization.workspaces.load(JSON.parse(objectType.program), workspace);
} catch {
// corrupt program, start fresh
}
}
isLoadingRef.current = false;
}, [objectType.id]);
return <div className="script-editor">
<div ref={containerRef} className="blockly-container" />
</div>;
});