85 lines
2.8 KiB
TypeScript
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>;
|
|
});
|