Skip to content

Commit f6b15d3

Browse files
committed
extract editor component and monaco setup
1 parent 46b9179 commit f6b15d3

File tree

2 files changed

+254
-116
lines changed

2 files changed

+254
-116
lines changed

packages/sandcastle/src/App.tsx

Lines changed: 28 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
22
import "./App.css";
33

4-
import Editor, { Monaco } from "@monaco-editor/react";
5-
import { editor, KeyCode } from "monaco-editor";
6-
import Gallery, { GalleryItem } from "./Gallery.js";
74
import { Button, Root } from "@itwin/itwinui-react/bricks";
85
import { decodeBase64Data, makeCompressedBase64String } from "./Helpers.ts";
6+
import Gallery, { GalleryItem } from "./Gallery.js";
97
import Bucket from "./Bucket.tsx";
8+
import SandcastleEditor, { SandcastleEditorRef } from "./SandcastleEditor.tsx";
109

1110
const defaultJsCode = `import * as Cesium from "cesium";
1211
@@ -25,22 +24,20 @@ function getBaseUrl() {
2524
return `${location.protocol}//${location.host}${location.pathname}`;
2625
}
2726

28-
const TYPES_URL = `${__PAGE_BASE_URL__}Source/Cesium.d.ts`;
29-
const SANDCASTLE_TYPES_URL = `templates/Sandcastle.d.ts`;
3027
const GALLERY_BASE = __GALLERY_BASE_URL__;
3128

29+
export type SandcastleAction =
30+
| { type: "reset" }
31+
| { type: "setCode"; code: string }
32+
| { type: "setHtml"; html: string }
33+
| { type: "runSandcastle" }
34+
| { type: "setAndRun"; code?: string; html?: string };
35+
3236
function App() {
33-
const [activeTab, setActiveTab] = useState<"js" | "html">("js");
3437
const [darkTheme, setDarkTheme] = useState(false);
3538

36-
const editorRef = useRef<editor.IStandaloneCodeEditor>(null);
39+
const editorRef = useRef<SandcastleEditorRef>(null);
3740

38-
type SandcastleAction =
39-
| { type: "reset" }
40-
| { type: "setCode"; code: string }
41-
| { type: "setHtml"; html: string }
42-
| { type: "runSandcastle" }
43-
| { type: "setAndRun"; code?: string; html?: string };
4441
type CodeState = {
4542
code: string;
4643
html: string;
@@ -105,87 +102,12 @@ function App() {
105102
dispatch({ type: "runSandcastle" });
106103
}
107104

108-
function handleEditorDidMount(
109-
editor: editor.IStandaloneCodeEditor,
110-
monaco: Monaco,
111-
) {
112-
editorRef.current = editor;
113-
114-
monaco.editor.addCommand({
115-
id: "run-cesium",
116-
run: () => {
117-
dispatch({ type: "runSandcastle" });
118-
},
119-
});
120-
121-
monaco.editor.addKeybindingRules([
122-
// Remove some default keybindings that get in the way
123-
// https://github.com/microsoft/monaco-editor/issues/102
124-
// disable show command center
125-
{ keybinding: KeyCode.F1, command: null },
126-
// disable show error command
127-
{ keybinding: KeyCode.F8, command: null },
128-
// disable toggle debugger breakpoint
129-
{ keybinding: KeyCode.F9, command: null },
130-
// disable go to definition to allow opening dev console
131-
{ keybinding: KeyCode.F12, command: null },
132-
// Set up our custom run command
133-
{ keybinding: KeyCode.F8, command: "run-cesium" },
134-
]);
135-
}
136-
137-
function handleEditorWillMount(monaco: Monaco) {
138-
// here is the monaco instance
139-
// do something before editor is mounted
140-
141-
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
142-
// TODO: pick what target we want, probably newer than ES2020 but TS was upset with that
143-
target: monaco.languages.typescript.ScriptTarget.ES2020,
144-
allowNonTsExtensions: true,
145-
});
146-
147-
setTypes(monaco);
148-
}
149-
150-
function handleChange(value: string = "") {
151-
if (activeTab === "js") {
152-
dispatch({ type: "setCode", code: value });
153-
} else {
154-
dispatch({ type: "setHtml", html: value });
155-
}
156-
}
157-
158-
async function setTypes(monaco: Monaco) {
159-
// https://microsoft.github.io/monaco-editor/playground.html?source=v0.52.2#example-extending-language-services-configure-javascript-defaults
160-
161-
const cesiumTypes = await (await fetch(TYPES_URL)).text();
162-
// define a "global" variable so types work even with out the import statement
163-
const cesiumTypesWithGlobal = `${cesiumTypes}\nvar Cesium: typeof import('cesium');`;
164-
monaco.languages.typescript.javascriptDefaults.addExtraLib(
165-
cesiumTypesWithGlobal,
166-
"ts:cesium.d.ts",
167-
);
168-
169-
const sandcastleTypes = await (await fetch(SANDCASTLE_TYPES_URL)).text();
170-
// surround in a module so the import statement works nicely
171-
// also define a "global" so types show even if you don't have the import
172-
const sandcastleModuleTypes = `declare module 'Sandcastle' {
173-
${sandcastleTypes}
174-
}
175-
var Sandcastle: typeof import('Sandcastle').default;`;
176-
177-
monaco.languages.typescript.javascriptDefaults.addExtraLib(
178-
sandcastleModuleTypes,
179-
"ts:sandcastle.d.ts",
180-
);
181-
}
182-
183105
function highlightLine(lineNumber: number) {
184106
console.log("would highlight line", lineNumber, "but not implemented yet");
185107
}
186108

187109
function formatJs() {
188-
editorRef.current?.getAction("editor.action.formatDocument")?.run();
110+
editorRef.current?.formatCode();
189111
}
190112

191113
function nextHighestVariableName(code: string, name: string) {
@@ -394,32 +316,19 @@ Sandcastle.addToolbarMenu(${variableName});`,
394316
<div className="spacer"></div>
395317
<Button onClick={() => setDarkTheme(!darkTheme)}>Swap Theme</Button>
396318
</div>
397-
<div className="editor-container">
398-
<div className="tabs">
399-
<Button
400-
disabled={activeTab === "js"}
401-
onClick={() => setActiveTab("js")}
402-
>
403-
Javascript
404-
</Button>
405-
<Button
406-
disabled={activeTab === "html"}
407-
onClick={() => setActiveTab("html")}
408-
>
409-
HTML/CSS
410-
</Button>
411-
</div>
412-
<Editor
413-
theme={darkTheme ? "vs-dark" : "light"}
414-
path={activeTab === "js" ? "script.js" : "index.html"}
415-
language={activeTab === "js" ? "javascript" : "html"}
416-
value={activeTab === "js" ? codeState.code : codeState.html}
417-
defaultValue={defaultJsCode}
418-
onMount={handleEditorDidMount}
419-
beforeMount={handleEditorWillMount}
420-
onChange={handleChange}
421-
/>
422-
</div>
319+
<SandcastleEditor
320+
ref={editorRef}
321+
darkTheme={darkTheme}
322+
onJsChange={(value: string = "") =>
323+
dispatch({ type: "setCode", code: value })
324+
}
325+
onHtmlChange={(value: string = "") =>
326+
dispatch({ type: "setHtml", html: value })
327+
}
328+
onRun={() => dispatch({ type: "runSandcastle" })}
329+
js={codeState.code}
330+
html={codeState.html}
331+
/>
423332
<div className="viewer-bucket">
424333
<Bucket
425334
code={codeState.committedCode}
@@ -436,7 +345,10 @@ Sandcastle.addToolbarMenu(${variableName});`,
436345
loadGalleryItem(item.id);
437346

438347
const searchParams = new URLSearchParams(window.location.search);
439-
if (searchParams.has("id") && searchParams.get("id") !== item.id) {
348+
if (
349+
!searchParams.has("id") ||
350+
(searchParams.has("id") && searchParams.get("id") !== item.id)
351+
) {
440352
// only push state if it's not the current url to prevent duplicated in history
441353
window.history.pushState({}, "", `${getBaseUrl()}?id=${item.id}`);
442354
}

0 commit comments

Comments
 (0)