diff --git a/src/components/CodeEmbed/index.jsx b/src/components/CodeEmbed/index.jsx index d0b39fe557..d3b8f07740 100644 --- a/src/components/CodeEmbed/index.jsx +++ b/src/components/CodeEmbed/index.jsx @@ -1,44 +1,37 @@ import { useState, useEffect, useRef } from "preact/hooks"; -import { useLiveRegion } from '../hooks/useLiveRegion'; +import { useLiveRegion } from "../hooks/useLiveRegion"; import CodeMirror, { EditorView } from "@uiw/react-codemirror"; import { javascript } from "@codemirror/lang-javascript"; +import { keymap } from "@codemirror/view"; +import { Prec, EditorState } from "@codemirror/state"; +import { insertTab } from "@codemirror/commands"; import { cdnLibraryUrl, cdnSoundUrl } from "@/src/globals/globals"; import { CodeFrame } from "./frame"; import { CopyCodeButton } from "../CopyCodeButton"; import CircleButton from "../CircleButton"; import { Icon } from "../Icon"; + /* * A more featured code embed component that uses CodeMirror - * - * Props: { - * initialValue?: string; - * editable: boolean; - * previewable: boolean; - * previewHeight?: number; - * previewWidth?: number; - * base?: string; - * lazyLoad?: boolean; - * TODO: refactor this prop behavior - * allowSideBySide?: boolean - * fullWidth?: boolean - * includeSound?: boolean - * } */ export const CodeEmbed = (props) => { const { ref: liveRegionRef, announce } = useLiveRegion(); const [rendered, setRendered] = useState(false); + const [enableTabIndent, setEnableTabIndent] = useState(false); + const initialCode = props.initialValue ?? ""; - // Source code from Google Docs sometimes uses a unicode non-breaking space - // instead of a normal one, but these break the code frame, so we replace them here. - // We also replace them in CodeFrame, but replacing here too ensures people don't - // accidentally copy-and-paste them out of the embedded editor. const [codeString, setCodeString] = useState( initialCode.replace(/\u00A0/g, " "), ); let { previewWidth, previewHeight } = props; - const canvasMatch = /createCanvas\(\s*(\d+),\s*(\d+)\s*(?:,\s*(?:P2D|WEBGL)\s*)?\)/m.exec(initialCode); + + const canvasMatch = + /createCanvas\(\s*(\d+),\s*(\d+)\s*(?:,\s*(?:P2D|WEBGL)\s*)?\)/m.exec( + initialCode, + ); + if (canvasMatch) { previewWidth = previewWidth || parseFloat(canvasMatch[1]); previewHeight = previewHeight || parseFloat(canvasMatch[2]); @@ -46,13 +39,15 @@ export const CodeEmbed = (props) => { const largeSketch = previewWidth && previewWidth > 770 - 60; - // Quick hack to make room for DOM that gets added below the canvas by default - const domMatch = /create(Button|Select|P|Div|Input|ColorPicker)/.exec(initialCode); + const domMatch = + /create(Button|Select|P|Div|Input|ColorPicker)/.exec(initialCode); + if (domMatch && previewHeight) { previewHeight += 100; } const codeFrameRef = useRef(null); + const [previewCodeString, setPreviewCodeString] = useState(codeString); const updateOrReRun = () => { if (codeString === previewCodeString) { @@ -64,12 +59,9 @@ export const CodeEmbed = (props) => { announce("Sketch is running"); }; - const [previewCodeString, setPreviewCodeString] = useState(codeString); - useEffect(() => { setRendered(true); - // Includes p5.min.js script to be used by `CodeFrame` iframe(s) if (!document.getElementById("p5ScriptTag")) { const p5ScriptElement = document.createElement("script"); p5ScriptElement.id = "p5ScriptTag"; @@ -80,13 +72,31 @@ export const CodeEmbed = (props) => { if (!rendered) return
; + const escToBlur = EditorView.domEventHandlers({ + keydown(event, view) { + if (event.key === "Escape") { + view.contentDOM.blur(); + return true; + } + return false; + }, + }); + return (