add save and reset button to playground
This commit is contained in:
parent
d04f61752d
commit
87f911e055
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useMonaco, type Monaco } from "@monaco-editor/react";
|
||||
import { useImmer, type ImmerHook, type Updater } from "use-immer";
|
||||
import { useImmer, type Updater } from "use-immer";
|
||||
|
||||
import "@styles/playground.scss";
|
||||
|
||||
|
@ -20,8 +20,8 @@ export type Directory = {
|
|||
};
|
||||
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
|
||||
|
||||
export default function Playground() {
|
||||
const [rootDir, updateRootDir] = useImmer({
|
||||
const FILE_STORAGE_KEY = "playground-files";
|
||||
const DEFAULT_FILES = {
|
||||
dirs: {
|
||||
src: {
|
||||
files: {
|
||||
|
@ -31,14 +31,7 @@ export default function Playground() {
|
|||
},
|
||||
},
|
||||
},
|
||||
dist: {
|
||||
files: {
|
||||
"test.mcfunction": {
|
||||
content: "",
|
||||
language: "mcfunction",
|
||||
},
|
||||
},
|
||||
},
|
||||
dist: {},
|
||||
},
|
||||
files: {
|
||||
"pack.toml": {
|
||||
|
@ -46,23 +39,66 @@ export default function Playground() {
|
|||
language: "toml",
|
||||
},
|
||||
},
|
||||
} as Directory);
|
||||
};
|
||||
|
||||
export default function Playground() {
|
||||
const [rootDir, updateRootDir] = useImmer(
|
||||
getStorageOrDefault(FILE_STORAGE_KEY, DEFAULT_FILES) as Directory
|
||||
);
|
||||
|
||||
const [fileName, setFileName] = useState("src/main.shu");
|
||||
const file = getFile(rootDir, fileName)!;
|
||||
const file = getFile(rootDir, fileName);
|
||||
|
||||
const onBuild: () => void = () => {
|
||||
const onBuild = () => {
|
||||
if (monaco) {
|
||||
console.log(getFiles(monaco));
|
||||
} else {
|
||||
console.error("monaco has not loaded");
|
||||
}
|
||||
};
|
||||
const onZip: () => void = () => {
|
||||
const onZip = () => {
|
||||
if (monaco) {
|
||||
loadFile(monaco, updateRootDir, {content: "zip"}, "dist/pack.zip");
|
||||
loadFile(
|
||||
monaco,
|
||||
updateRootDir,
|
||||
{ content: "zip" },
|
||||
"dist/pack.zip"
|
||||
);
|
||||
} else {
|
||||
console.error("onZip not set");}
|
||||
console.error("onZip not set");
|
||||
}
|
||||
};
|
||||
const onSave = () => {
|
||||
if (monaco) {
|
||||
const currentFiles = getFiles(monaco);
|
||||
updateRootDir((dir) => {
|
||||
dir.dirs = currentFiles.dirs;
|
||||
dir.files = currentFiles.files;
|
||||
});
|
||||
window.localStorage.setItem(
|
||||
FILE_STORAGE_KEY,
|
||||
JSON.stringify(currentFiles)
|
||||
);
|
||||
}
|
||||
};
|
||||
const onReset = () => {
|
||||
if (monaco) {
|
||||
monaco.editor.getModels().forEach((model) => {
|
||||
if (model.uri.path != "/src/main.shu") {
|
||||
model.dispose();
|
||||
} else {
|
||||
model.setValue(mainFileContent);
|
||||
}
|
||||
});
|
||||
|
||||
updateRootDir((dir) => {
|
||||
dir.dirs = DEFAULT_FILES.dirs;
|
||||
dir.files = DEFAULT_FILES.files;
|
||||
});
|
||||
|
||||
loadFiles(monaco, updateRootDir, DEFAULT_FILES);
|
||||
setFileName("src/main.shu");
|
||||
}
|
||||
};
|
||||
|
||||
const monaco = useMonaco();
|
||||
|
@ -82,14 +118,19 @@ export default function Playground() {
|
|||
marginTop: "0.5cm",
|
||||
}}
|
||||
>
|
||||
<Header onBuild={onBuild} onZip={onZip} />
|
||||
<Header
|
||||
onSave={onSave}
|
||||
onReset={onReset}
|
||||
onBuild={onBuild}
|
||||
onZip={onZip}
|
||||
/>
|
||||
<FileView
|
||||
className="file-view"
|
||||
root={rootDir}
|
||||
fileName={fileName}
|
||||
setSelectedFileName={setFileName}
|
||||
/>
|
||||
<Editor fileName={fileName} file={file} />
|
||||
<Editor fileName={fileName} file={file ?? undefined} />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
@ -111,7 +152,7 @@ function getFiles(monaco: Monaco): Directory {
|
|||
dir.dirs[part] = {};
|
||||
}
|
||||
|
||||
dir = dir.dirs[part].files ?? {};
|
||||
dir = dir.dirs[part];
|
||||
}
|
||||
|
||||
if (!dir.files) {
|
||||
|
@ -147,12 +188,20 @@ function getFile(root: Directory, path: string): File | null {
|
|||
return root.files?.[path] ?? null;
|
||||
}
|
||||
|
||||
function loadFiles(monaco: Monaco, updater: Updater<Directory>, dir: Directory, prefix = "") {
|
||||
function loadFiles(
|
||||
monaco: Monaco,
|
||||
updater: Updater<Directory>,
|
||||
dir: Directory,
|
||||
prefix = ""
|
||||
) {
|
||||
for (const [name, d] of Object.entries(dir.dirs ?? {})) {
|
||||
loadFiles(monaco, updater, d, prefix + name + "/");
|
||||
updater(dir => {
|
||||
updater((dir) => {
|
||||
let current = dir;
|
||||
for(const part of [...prefix.split("/").filter(s => s !== ""), name]) {
|
||||
for (const part of [
|
||||
...prefix.split("/").filter((s) => s !== ""),
|
||||
name,
|
||||
]) {
|
||||
if (!current.dirs) {
|
||||
current.dirs = {};
|
||||
}
|
||||
|
@ -166,17 +215,21 @@ function loadFiles(monaco: Monaco, updater: Updater<Directory>, dir: Directory,
|
|||
}
|
||||
}
|
||||
|
||||
function loadFile(monaco: Monaco, updater: Updater<Directory>, file: File, name: string) {
|
||||
function loadFile(
|
||||
monaco: Monaco,
|
||||
updater: Updater<Directory>,
|
||||
file: File,
|
||||
name: string
|
||||
) {
|
||||
const uri = monaco.Uri.parse(name);
|
||||
if (!monaco.editor.getModel(uri)) {
|
||||
monaco.editor.createModel(file.content, file.language, uri);
|
||||
}
|
||||
updater(dir => {
|
||||
updater((dir) => {
|
||||
let current = dir;
|
||||
const parts = name.split("/").filter(s => s !== "");
|
||||
const parts = name.split("/").filter((s) => s !== "");
|
||||
const last = parts.pop()!;
|
||||
for (const part of parts) {
|
||||
console.log(part);
|
||||
if (!current.dirs) {
|
||||
current.dirs = {};
|
||||
}
|
||||
|
@ -189,4 +242,11 @@ function loadFile(monaco: Monaco, updater: Updater<Directory>, file: File, name:
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function getStorageOrDefault(key: string, def: any) {
|
||||
const item = window.localStorage.getItem(key);
|
||||
if (item) {
|
||||
return JSON.parse(item);
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ import { ThemeProvider } from "@mui/material";
|
|||
|
||||
import { customTheme } from "@utils/material-ui-theme";
|
||||
|
||||
export default function SplitButton({
|
||||
export default function DropdownButton({
|
||||
options,
|
||||
children,
|
||||
onClick,
|
||||
visible,
|
||||
style,
|
||||
}: {
|
||||
options: [string, React.MouseEventHandler<HTMLLIElement>][];
|
||||
children: React.ReactNode;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
visible: [React.ReactNode, React.MouseEventHandler<HTMLButtonElement>][];
|
||||
style?: React.CSSProperties;
|
||||
}) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef<HTMLDivElement>(null);
|
||||
|
@ -53,10 +53,11 @@ export default function SplitButton({
|
|||
variant="contained"
|
||||
ref={anchorRef}
|
||||
aria-label="Button group with a nested menu"
|
||||
style={style}
|
||||
>
|
||||
<Button onClick={onClick}>
|
||||
{children}
|
||||
</Button>
|
||||
{visible.map(([children, onClick], index) => {
|
||||
return <Button key={index} onClick={onClick}>{children}</Button>;
|
||||
})}
|
||||
<Button
|
||||
size="small"
|
||||
aria-controls={open ? "split-button-menu" : undefined}
|
|
@ -13,7 +13,7 @@ export default function Editor({
|
|||
file,
|
||||
}: {
|
||||
fileName: string;
|
||||
file: File;
|
||||
file?: File;
|
||||
}) {
|
||||
const [highlighter, setHighlighter] = useState<Highlighter | null>(null);
|
||||
|
||||
|
@ -48,8 +48,8 @@ export default function Editor({
|
|||
height="70vh"
|
||||
theme="vs-dark"
|
||||
path={fileName}
|
||||
defaultLanguage={file.language}
|
||||
defaultValue={file.content}
|
||||
defaultLanguage={file?.language}
|
||||
defaultValue={file?.content}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,15 +1,37 @@
|
|||
import SplitButton from "./SplitButton";
|
||||
|
||||
export default function Header({onBuild, onZip}: {onBuild: () => void; onZip: () => void;}){
|
||||
import DropdownButton from "./DropdownButton";
|
||||
|
||||
export default function Header({
|
||||
onSave,
|
||||
onReset,
|
||||
onBuild,
|
||||
onZip,
|
||||
}: {
|
||||
onSave: () => void;
|
||||
onReset: () => void;
|
||||
onBuild: () => void;
|
||||
onZip: () => void;
|
||||
}) {
|
||||
return (
|
||||
<header style={{
|
||||
<header
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "0.5cm",
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<h1 id="_top">Playground</h1>
|
||||
<SplitButton onClick={onBuild} options={[["Download zip", onZip]]}>Build</SplitButton>
|
||||
<div className="buttons" style={{ height: "100%" }}>
|
||||
<DropdownButton
|
||||
style={{ height: "100%", marginRight: "0.5cm"}}
|
||||
visible={[["Save", onSave]]}
|
||||
options={[["Reset", onReset]]}
|
||||
/>
|
||||
<DropdownButton
|
||||
style={{ height: "100%" }}
|
||||
visible={[["Build", onBuild]]}
|
||||
options={[["Download zip", onZip]]}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue