implement file creation

This commit is contained in:
Moritz Hölting 2024-06-23 21:22:03 +02:00
parent 7096d02e8c
commit f798b55e7a
9 changed files with 284 additions and 24 deletions

View File

@ -177,18 +177,31 @@ export default function Playground({ lang }: { lang: PlaygroundLang }) {
root={rootDir}
selectedFileName={fileName}
setSelectedFileName={setFileName}
addFile={(name) => {
if (monaco) {
loadFile(
monaco,
updateRootDir,
{ content: "" },
name
);
}
}}
deleteFile={(name) => {
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("");
if (name.endsWith("/")) {
deleteDir(monaco, updateRootDir, name);
} else {
deleteFile(monaco, updateRootDir, name);
if (name === fileName) {
const newFile = monaco.editor
.getModels()[0]
?.uri.path.slice(1);
if (newFile) {
setFileName(newFile);
} else {
setFileName("");
}
}
}
}
@ -384,7 +397,56 @@ function deleteFile(monaco: Monaco, updater: Updater<Directory>, name: string) {
});
}
function renameFile(monaco: Monaco, updater: Updater<Directory>, oldName: string, newName: string) {
function deleteDir(monaco: Monaco, updater: Updater<Directory>, path: string) {
const parts = path.split("/").filter((s) => s !== "");
const last = parts.pop()!;
let current = getFiles(monaco);
for (const part of parts) {
if (!current.dirs) {
current.dirs = {};
}
if (!current.dirs[part]) {
current.dirs[part] = {};
}
current = current.dirs[part];
}
if (current.dirs) {
for (const [name, _] of Object.entries(current.dirs ?? {})) {
deleteDir(monaco, updater, path + name + "/");
}
for (const [name, _] of Object.entries(current.files ?? {})) {
deleteFile(monaco, updater, path + name);
}
delete current.dirs[last];
}
updater((dir) => {
let current = dir;
for (const part of parts) {
if (!current.dirs) {
current.dirs = {};
}
if (!current.dirs[part]) {
current.dirs[part] = {};
}
current = current.dirs[part];
}
if (current.dirs) {
delete current.dirs[last];
}
});
}
function renameFile(
monaco: Monaco,
updater: Updater<Directory>,
oldName: string,
newName: string
) {
const file = getFile(getFiles(monaco), oldName);
if (file) {
deleteFile(monaco, updater, oldName);

View File

@ -0,0 +1,69 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
TextField,
} from "@mui/material";
import type { PlaygroundExplorerLang } from "@utils/playground";
export default function AddFileDialog({
open,
handleClose,
addFile,
lang,
defaultPath,
}: {
open: boolean;
defaultPath: string;
lang: PlaygroundExplorerLang;
handleClose: () => void;
addFile: (filepath: string) => void;
}) {
return (
<Dialog
open={open}
onClose={handleClose}
PaperProps={{
component: "form",
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const formJson = Object.fromEntries(
(formData as any).entries()
);
const filepath = formJson.filepath;
if (addFile) {
addFile(filepath);
}
handleClose();
},
}}
>
<DialogTitle>{lang.menu.add}</DialogTitle>
<DialogContent>
<DialogContentText>
{lang.menu.addPrompt.message}
</DialogContentText>
<TextField
autoFocus
required
margin="dense"
id="filepath"
name="filepath"
label={lang.menu.addPrompt.label}
type="text"
fullWidth
variant="filled"
defaultValue={defaultPath}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>{lang.menu.cancel}</Button>
<Button type="submit">{lang.menu.add}</Button>
</DialogActions>
</Dialog>
);
}

View File

@ -1,10 +1,19 @@
import type { Directory, PlaygroundExplorerLang, SetState } from "@utils/playground";
import type {
Directory,
PlaygroundExplorerLang,
SetState,
} from "@utils/playground";
import React, { useState } from "react";
import {
GoChevronDown as ChevDown,
GoChevronRight as ChevRight,
} from "react-icons/go";
import FileElement from "./FileElement";
import {
Menu,
MenuItem,
} from "@mui/material";
import AddFileDialog from "./AddFileDialog";
export default function DirElement({
name,
@ -14,6 +23,7 @@ export default function DirElement({
selectedFileName,
lang,
setSelectedFileName,
addFile,
deleteFile,
renameFile,
}: {
@ -24,6 +34,7 @@ export default function DirElement({
selectedFileName: string;
lang: PlaygroundExplorerLang;
setSelectedFileName: SetState<string>;
addFile: (name: string) => void;
deleteFile: (name: string) => void;
renameFile: (oldName: string, newName: string) => void;
}) {
@ -37,11 +48,32 @@ export default function DirElement({
Object.keys(currentDir.dirs ?? {}).length > 0 ||
Object.keys(currentDir.files ?? {}).length > 0;
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const contextOpen = Boolean(anchorEl);
const handleContext = (event: React.MouseEvent<HTMLElement>) => {
event.preventDefault();
setAnchorEl(event.currentTarget);
};
const handleContextClose = () => {
setAnchorEl(null);
};
const [addOpen, setAddOpen] = React.useState(false);
const handleAddClose = () => {
setAddOpen(false);
};
const onDelete = () => {
deleteFile(fullPath + "/");
};
return (
<div key={name} className="dir">
<button
style={{ display: "block" }}
onClick={() => setCollapsed(!collapsed)}
onContextMenu={handleContext}
>
{collapsed ? (
<ChevRight
@ -53,6 +85,30 @@ export default function DirElement({
)}{" "}
{name + "/" + (collapsed && hasChildren ? "..." : "")}
</button>
<Menu
anchorEl={anchorEl}
open={contextOpen}
onClose={handleContextClose}
>
<MenuItem
disabled={(fullPath + "/").startsWith("dist/")}
onClick={() => {
handleContextClose();
setAddOpen(true);
}}
>
{lang.menu.add}
</MenuItem>
<MenuItem
onClick={() => {
handleContextClose();
onDelete?.();
}}
>
{lang.menu.delete}
</MenuItem>
</Menu>
<AddFileDialog lang={lang} defaultPath={fullPath + "/"} open={addOpen} addFile={addFile} handleClose={handleAddClose} />
<div style={{ marginLeft: "0.5cm" }} className="dirChildren">
{collapsed ? null : (
<div>
@ -66,6 +122,7 @@ export default function DirElement({
dir={dir}
selectedFileName={selectedFileName}
lang={lang}
addFile={addFile}
setSelectedFileName={
setSelectedFileName
}
@ -77,20 +134,21 @@ export default function DirElement({
)}
{Object.entries(currentDir.files ?? {}).map(
([currentName, _]) => {
const currentPath = fullPath + "/" + currentName;
const currentPath =
fullPath + "/" + currentName;
return (
<FileElement
key={currentName}
fullPath={currentPath}
name={currentName}
isSelected={selectedFileName == currentPath}
isSelected={
selectedFileName == currentPath
}
lang={lang}
onClick={() =>
setSelectedFileName(currentPath)
}
onDelete={() =>
deleteFile(currentPath)
}
onDelete={() => deleteFile(currentPath)}
renameFile={renameFile}
/>
);

View File

@ -1,4 +1,14 @@
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Menu, MenuItem, TextField } from "@mui/material";
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";
@ -50,10 +60,11 @@ export default function FileElement({
onClose={handleContextClose}
>
<MenuItem
onClick={(ev) => {
onClick={() => {
handleContextClose();
setRenameOpen(true);
}}
disabled={fullPath.startsWith("dist/")}
>
{lang.menu.rename}
</MenuItem>
@ -85,7 +96,9 @@ export default function FileElement({
},
}}
>
<DialogTitle>{lang.menu.rename} - {fullPath}</DialogTitle>
<DialogTitle>
{lang.menu.rename} - {fullPath}
</DialogTitle>
<DialogContent>
<DialogContentText>
{lang.menu.renamePrompt.message}
@ -104,7 +117,9 @@ export default function FileElement({
/>
</DialogContent>
<DialogActions>
<Button onClick={handleRenameClose}>{lang.menu.cancel}</Button>
<Button onClick={handleRenameClose}>
{lang.menu.cancel}
</Button>
<Button type="submit">{lang.menu.rename}</Button>
</DialogActions>
</Dialog>

View File

@ -5,12 +5,16 @@ import type {
} from "@utils/playground";
import DirElement from "./DirElement";
import FileElement from "./FileElement";
import { Menu, MenuItem } from "@mui/material";
import React, { useState } from "react";
import AddFileDialog from "./AddFileDialog";
export default function FileView({
lang,
root,
selectedFileName,
setSelectedFileName,
addFile,
deleteFile,
renameFile,
className,
@ -19,13 +23,45 @@ export default function FileView({
root: Directory;
selectedFileName: string;
setSelectedFileName: SetState<string>;
addFile: (name: string) => void;
deleteFile: (name: string) => void;
renameFile: (oldName: string, newName: string) => void;
className?: string;
}) {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const contextOpen = Boolean(anchorEl);
const handleContext = (event: React.MouseEvent<HTMLElement>) => {
event.preventDefault();
setAnchorEl(event.currentTarget);
};
const handleContextClose = () => {
setAnchorEl(null);
};
const [addOpen, setAddOpen] = React.useState(false);
const handleAddClose = () => {
setAddOpen(false);
};
return (
<div className={className}>
<h3>{lang.title}</h3>
<h3 onContextMenu={handleContext}>{lang.title}</h3>
<Menu
anchorEl={anchorEl}
open={contextOpen}
onClose={handleContextClose}
>
<MenuItem
onClick={() => {
handleContextClose();
setAddOpen(true);
}}
>
{lang.menu.add}
</MenuItem>
</Menu>
<AddFileDialog lang={lang} defaultPath="" addFile={addFile} handleClose={handleAddClose} open={addOpen} />
<div className="entries">
{Object.entries(root.dirs ?? {}).map(([name, dir]) => {
return (
@ -37,6 +73,7 @@ export default function FileView({
selectedFileName={selectedFileName}
lang={lang}
setSelectedFileName={setSelectedFileName}
addFile={addFile}
deleteFile={deleteFile}
renameFile={renameFile}
/>

View File

@ -16,6 +16,11 @@ const lang: PlaygroundLang = {
explorer: {
title: "Dateien",
menu: {
add: "Datei hinzufügen",
addPrompt: {
label: "Dateipfad",
message: "Pfad eingeben, an dem die Datei erstellt werden soll."
},
delete: "Löschen",
rename: "Umbenennen",
renamePrompt: {

View File

@ -16,6 +16,11 @@ const lang: PlaygroundLang = {
explorer: {
title: "Explorer",
menu: {
add: "Add file",
addPrompt: {
label: "File path",
message: "Enter the path you want the new file to be created at."
},
delete: "Delete",
rename: "Rename",
renamePrompt: {

View File

@ -16,6 +16,10 @@
overflow-x: hidden;
overflow-y: auto;
> h3 {
cursor: pointer;
}
.entries {
button {
border: none;

View File

@ -15,6 +15,11 @@ export type PlaygroundHeaderLang = {
export type PlaygroundExplorerLang = {
title: string;
menu: {
add: string;
addPrompt: {
message: string;
label: string;
}
rename: string;
renamePrompt: {
message: string;