From 7096d02e8c7e58ae3a687b0c773fae682f9b7f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sun, 23 Jun 2024 20:28:12 +0200 Subject: [PATCH] implement rename and delete for files --- README.md | 12 +- src/components/Playground.tsx | 77 +++++++++++- .../{FileView.tsx => FileView/DirElement.tsx} | 100 +++++----------- .../playground/FileView/FileElement.tsx | 113 ++++++++++++++++++ src/components/playground/FileView/index.tsx | 71 +++++++++++ src/pages/de/playground.astro | 9 ++ src/pages/playground.astro | 9 ++ src/styles/playground.scss | 19 +-- src/utils/playground.ts | 9 ++ src/wasm/webcompiler/Cargo.toml | 2 +- 10 files changed, 322 insertions(+), 99 deletions(-) rename src/components/playground/{FileView.tsx => FileView/DirElement.tsx} (55%) create mode 100644 src/components/playground/FileView/FileElement.tsx create mode 100644 src/components/playground/FileView/index.tsx diff --git a/README.md b/README.md index f8cd85c..3333f01 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ All commands are run from the root of the project, from a terminal: | Command | Action | | :------------------------ | :----------------------------------------------- | -| `npm install` | Installs dependencies | -| `npm run dev` | Starts local dev server at `localhost:4321` | -| `npm run build` | Build your production site to `./dist/` | -| `npm run preview` | Preview your build locally, before deploying | -| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | -| `npm run astro -- --help` | Get help using the Astro CLI | \ No newline at end of file +| `pnpm install` | Installs dependencies | +| `pnpm run dev` | Starts local dev server at `localhost:4321` | +| `pnpm run build` | Build your production site to `./dist/` | +| `pnpm run preview` | Preview your build locally, before deploying | +| `pnpm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `pnpm run astro -- --help` | Get help using the Astro CLI | \ No newline at end of file diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index c7b55b5..419b006 100644 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -175,8 +175,32 @@ export default function Playground({ lang }: { lang: PlaygroundLang }) { { + if (monaco) { + console.log(name); + deleteFile(monaco, updateRootDir, name); + if (name === fileName) { + const newFile = monaco.editor + .getModels()[0] + ?.uri.path.slice(1); + if (newFile) { + setFileName(newFile); + } else { + setFileName(""); + } + } + } + }} + renameFile={(oldName, newName) => { + if (monaco) { + renameFile(monaco, updateRootDir, oldName, newName); + if (oldName === fileName) { + setFileName(newName); + } + } + }} lang={lang.explorer} /> { if (dir) { @@ -295,7 +330,10 @@ function loadFile( if (!current.files) { current.files = {}; } - current.files[last] = file; + current.files[last] = { + content: file.content, + language: lang, + }; } }); } @@ -320,3 +358,36 @@ function jsonReplacer(key: any, value: any): any { return value; } } + +function deleteFile(monaco: Monaco, updater: Updater, name: string) { + const uri = monaco.Uri.parse(name); + const model = monaco.editor.getModel(uri); + if (model) { + model.dispose(); + } + updater((dir) => { + let current = dir; + const parts = name.split("/").filter((s) => s !== ""); + const last = parts.pop()!; + for (const part of parts) { + if (!current.dirs) { + current.dirs = {}; + } + if (!current.dirs[part]) { + current.dirs[part] = {}; + } + current = current.dirs[part]; + } + if (current.files) { + delete current.files[last]; + } + }); +} + +function renameFile(monaco: Monaco, updater: Updater, oldName: string, newName: string) { + const file = getFile(getFiles(monaco), oldName); + if (file) { + deleteFile(monaco, updater, oldName); + loadFile(monaco, updater, file, newName); + } +} \ No newline at end of file diff --git a/src/components/playground/FileView.tsx b/src/components/playground/FileView/DirElement.tsx similarity index 55% rename from src/components/playground/FileView.tsx rename to src/components/playground/FileView/DirElement.tsx index 3e763a4..a82826e 100644 --- a/src/components/playground/FileView.tsx +++ b/src/components/playground/FileView/DirElement.tsx @@ -4,86 +4,31 @@ import { GoChevronDown as ChevDown, GoChevronRight as ChevRight, } from "react-icons/go"; +import FileElement from "./FileElement"; -export default function FileView({ - lang, - root, - fileName, - setSelectedFileName, - className, -}: { - lang: PlaygroundExplorerLang; - root: Directory; - fileName: string; - setSelectedFileName: SetState; - className?: string; -}) { - return ( -
-

{lang.title}

-
- {Object.entries(root.dirs ?? {}).map(([name, dir]) => { - return ( - - ); - })} - {Object.entries(root.files ?? {}).map(([name, _]) => { - return ( - - setSelectedFileName(name)} - /> - - ); - })} -
-
- ); -} - -function FileElement({ - name, - disabled, - onClick, -}: { - name: string; - disabled: boolean; - onClick?: React.MouseEventHandler; -}) { - return ( - - ); -} - -function DirElement({ +export default function DirElement({ name, + fullPath, dir: currentDir, collapsed: pCollapsed, - fileName, + selectedFileName, + lang, setSelectedFileName, + deleteFile, + renameFile, }: { name: string; + fullPath: string; dir: Directory; collapsed?: boolean; - fileName: string; + selectedFileName: string; + lang: PlaygroundExplorerLang; setSelectedFileName: SetState; + deleteFile: (name: string) => void; + renameFile: (oldName: string, newName: string) => void; }) { const [collapsed, setCollapsed] = useState(pCollapsed ?? false); - const modSetSelectedFileName: SetState = (selected) => { - setSelectedFileName(name + "/" + selected); - }; - const chevStyles: React.CSSProperties = { marginBottom: "-2px", }; @@ -117,27 +62,36 @@ function DirElement({ ); } )} {Object.entries(currentDir.files ?? {}).map( ([currentName, _]) => { + const currentPath = fullPath + "/" + currentName; return ( - modSetSelectedFileName(currentName) + setSelectedFileName(currentPath) } + onDelete={() => + deleteFile(currentPath) + } + renameFile={renameFile} /> ); } diff --git a/src/components/playground/FileView/FileElement.tsx b/src/components/playground/FileView/FileElement.tsx new file mode 100644 index 0000000..c91d2c1 --- /dev/null +++ b/src/components/playground/FileView/FileElement.tsx @@ -0,0 +1,113 @@ +import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Menu, MenuItem, TextField } from "@mui/material"; +import type { PlaygroundExplorerLang } from "@utils/playground"; +import React from "react"; +import { useState } from "react"; + +export default function FileElement({ + name, + fullPath, + isSelected, + lang, + onClick, + renameFile, + onDelete, +}: { + name: string; + fullPath: string; + isSelected: boolean; + lang: PlaygroundExplorerLang; + onClick?: React.MouseEventHandler; + renameFile?: (oldName: string, newName: string) => void; + onDelete?: React.MouseEventHandler; +}) { + const [anchorEl, setAnchorEl] = useState(null); + const contextOpen = Boolean(anchorEl); + const handleContext = (event: React.MouseEvent) => { + event.preventDefault(); + setAnchorEl(event.currentTarget); + }; + const handleContextClose = () => { + setAnchorEl(null); + }; + const [renameOpen, setRenameOpen] = React.useState(false); + + const handleRenameClose = () => { + setRenameOpen(false); + }; + + return ( +
+ + + { + handleContextClose(); + setRenameOpen(true); + }} + > + {lang.menu.rename} + + { + handleContextClose(); + onDelete?.(ev); + }} + > + {lang.menu.delete} + + + ) => { + event.preventDefault(); + const formData = new FormData(event.currentTarget); + const formJson = Object.fromEntries( + (formData as any).entries() + ); + const filepath = formJson.filepath; + if (renameFile) { + renameFile(fullPath, filepath); + } + handleRenameClose(); + }, + }} + > + {lang.menu.rename} - {fullPath} + + + {lang.menu.renamePrompt.message} + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/src/components/playground/FileView/index.tsx b/src/components/playground/FileView/index.tsx new file mode 100644 index 0000000..7d614c2 --- /dev/null +++ b/src/components/playground/FileView/index.tsx @@ -0,0 +1,71 @@ +import type { + Directory, + PlaygroundExplorerLang, + SetState, +} from "@utils/playground"; +import DirElement from "./DirElement"; +import FileElement from "./FileElement"; + +export default function FileView({ + lang, + root, + selectedFileName, + setSelectedFileName, + deleteFile, + renameFile, + className, +}: { + lang: PlaygroundExplorerLang; + root: Directory; + selectedFileName: string; + setSelectedFileName: SetState; + deleteFile: (name: string) => void; + renameFile: (oldName: string, newName: string) => void; + className?: string; +}) { + return ( +
+

{lang.title}

+
+ {Object.entries(root.dirs ?? {}).map(([name, dir]) => { + return ( + + ); + })} + {Object.entries(root.files ?? {}).map(([name, _]) => { + const isSelected = selectedFileName == name; + return ( + + {} + : () => setSelectedFileName(name) + } + onDelete={() => deleteFile(name)} + renameFile={renameFile} + /> + + ); + })} +
+
+ ); +} + + + diff --git a/src/pages/de/playground.astro b/src/pages/de/playground.astro index e8ed2a3..8a40dde 100644 --- a/src/pages/de/playground.astro +++ b/src/pages/de/playground.astro @@ -15,6 +15,15 @@ const lang: PlaygroundLang = { }, explorer: { title: "Dateien", + menu: { + delete: "Löschen", + rename: "Umbenennen", + renamePrompt: { + label: "Neuer Dateipfad", + message: "Pfad eingeben, zu dem die Datei umbenannt/verschoben werden soll." + }, + cancel: "Abbrechen", + } }, }; --- diff --git a/src/pages/playground.astro b/src/pages/playground.astro index 5ab4ec2..bf1e009 100644 --- a/src/pages/playground.astro +++ b/src/pages/playground.astro @@ -15,6 +15,15 @@ const lang: PlaygroundLang = { }, explorer: { title: "Explorer", + menu: { + delete: "Delete", + rename: "Rename", + renamePrompt: { + label: "New file path", + message: "Enter the path you want the file to be renamed/moved to." + }, + cancel: "Cancel", + } }, }; --- diff --git a/src/styles/playground.scss b/src/styles/playground.scss index 3a05268..140a032 100644 --- a/src/styles/playground.scss +++ b/src/styles/playground.scss @@ -25,13 +25,12 @@ cursor: pointer; &:hover { - background-color: #444444; + background-color: var(--sl-color-gray-5); } - &:disabled { - cursor: default; + &.selected { color: var(--sl-color-text); - background-color: #333; + background-color: var(--sl-color-gray-6); } } } @@ -39,16 +38,4 @@ > .editor { grid-area: editor; } -} - -:root[data-theme='light'] { - .playground > .file-view .entries button { - &:hover { - background-color: var(--sl-color-gray-5); - } - - &:disabled { - background-color: var(--sl-color-gray-6); - } - } } \ No newline at end of file diff --git a/src/utils/playground.ts b/src/utils/playground.ts index 06fec04..fc2ab8b 100644 --- a/src/utils/playground.ts +++ b/src/utils/playground.ts @@ -14,6 +14,15 @@ export type PlaygroundHeaderLang = { }; export type PlaygroundExplorerLang = { title: string; + menu: { + rename: string; + renamePrompt: { + message: string; + label: string; + } + delete: string; + cancel: string; + } }; export type File = { diff --git a/src/wasm/webcompiler/Cargo.toml b/src/wasm/webcompiler/Cargo.toml index ee50753..cac7d1a 100644 --- a/src/wasm/webcompiler/Cargo.toml +++ b/src/wasm/webcompiler/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2.92" -shulkerscript = { git = "https://github.com/moritz-hoelting/shulkerscript-lang.git", default-features = false, features = ["serde", "shulkerbox"], rev = "af544ac79eea4498ef4563acfb7e8dd14ec5c84e" } +shulkerscript = { git = "https://github.com/moritz-hoelting/shulkerscript-lang.git", default-features = false, features = ["serde", "shulkerbox", "lua"], rev = "af544ac79eea4498ef4563acfb7e8dd14ec5c84e" } serde = "1.0" serde-wasm-bindgen = "0.6.5" anyhow = "1.0.86"