From 8713d95bfd3d6f61256b854e52dee466c101ef3a Mon Sep 17 00:00:00 2001 From: lajbel Date: Mon, 14 Oct 2024 22:14:17 -0300 Subject: [PATCH] chore: simplified folders --- src/components/FileTree/FileEntry.tsx | 96 ++++++++++++++++--- src/components/FileTree/FileFold.tsx | 82 ++++++++++++++++ src/components/FileTree/FileFolder.tsx | 56 ----------- src/components/FileTree/FileToolbar.tsx | 24 +++-- src/components/FileTree/FileTree.tsx | 58 +++-------- .../Playground/WorkspaceExample.tsx | 3 +- src/components/Toolbar/ToolbarToolsMenu.tsx | 2 +- src/components/UI/View.tsx | 30 ++++++ src/stores/storage/files.ts | 16 +++- 9 files changed, 241 insertions(+), 126 deletions(-) create mode 100644 src/components/FileTree/FileFold.tsx delete mode 100644 src/components/FileTree/FileFolder.tsx create mode 100644 src/components/UI/View.tsx diff --git a/src/components/FileTree/FileEntry.tsx b/src/components/FileTree/FileEntry.tsx index 0219330..f89cb0e 100644 --- a/src/components/FileTree/FileEntry.tsx +++ b/src/components/FileTree/FileEntry.tsx @@ -6,6 +6,7 @@ import { cn } from "../../util/cn"; import { removeExtension } from "../../util/removeExtensions"; import "./FileEntry.css"; import { useEditor } from "../../hooks/useEditor"; +import { debug } from "../../util/logs"; type Props = { file: File; @@ -18,8 +19,28 @@ const logoByKind = { assets: assets.assetbrew.outlined, }; -const FileEntry: FC = ({ file }) => { - const { removeFile } = useProject(); +const FileButton: FC<{ + onClick: MouseEventHandler; + icon: keyof typeof assets; + rotate?: 0 | 90 | 180 | 270; +}> = (props) => { + return ( + + ); +}; + +export const FileEntry: FC = ({ file }) => { + const { removeFile, project, setProject } = useProject(); const { getRuntime, setCurrentFile } = useEditor(); const handleClick: MouseEventHandler = () => { @@ -39,6 +60,56 @@ const FileEntry: FC = ({ file }) => { } }; + const handleMoveUp: MouseEventHandler = (e) => { + e.stopPropagation(); + + // order the map with the file one step up + const files = project.files; + const order = Array.from(files.keys()); + const index = order.indexOf(file.path); + + if (index === 0) return; + + const newOrder = [...order]; + newOrder.splice(index, 1); + + newOrder.splice(index - 1, 0, file.path); + + const newFiles = new Map( + newOrder.map((path) => [path, files.get(path)!]), + ); + + setProject({ + ...project, + files: newFiles, + }); + }; + + const handleMoveDown: MouseEventHandler = (e) => { + e.stopPropagation(); + + // order the map with the file one step down + const files = project.files; + const order = Array.from(files.keys()); + const index = order.indexOf(file.path); + + if (index === order.length - 1) return; + + const newOrder = [...order]; + newOrder.splice(index, 1); + + newOrder.splice(index + 1, 0, file.path); + + const newFiles = new Map( + newOrder.map((path) => [path, files.get(path)!]), + ); + + setProject({ + ...project, + files: newFiles, + }); + }; + return (
= ({ file }) => { onClick={handleClick} data-file-kind={file.kind} > - + {removeExtension(file.name)}
- + icon="trash" + /> +
= ({ file }) => {
); }; - -export default FileEntry; diff --git a/src/components/FileTree/FileFold.tsx b/src/components/FileTree/FileFold.tsx new file mode 100644 index 0000000..5a2e3f5 --- /dev/null +++ b/src/components/FileTree/FileFold.tsx @@ -0,0 +1,82 @@ +import { assets } from "@kaplayjs/crew"; +import { type FC, type PropsWithChildren, useMemo, useState } from "react"; +import { cn } from "../../util/cn"; +import { FileToolbar } from "./FileToolbar"; +import "./FileFolder.css"; +import { useProject } from "../../hooks/useProject"; +import type { FileFolder, FileKind } from "../../stores/storage/files"; +import { FileEntry } from "./FileEntry"; + +type Props = PropsWithChildren<{ + level: 0 | 1 | 2; + title?: string; + toolbar?: boolean; + /** Kind of files on Folder */ + kind?: FileKind; + /** Folder */ + folder: FileFolder; +}>; + +const paddingLevels = { + 0: "pl-0", + 1: "pl-4", + 2: "pl-8", +}; + +export const FileFold: FC = (props) => { + const [folded, setFolded] = useState(false); + const { getFilesByFolder } = useProject(); + const files = useMemo(() => getFilesByFolder(props.folder), [props.folder]); + + return ( +
+
+ {props.title && ( +

{props.title}

+ )} + + {(props.toolbar && props.kind) && ( + + + + )} +
+ +
    + {files.length === 0 + ? ( +
  • + Create an {props.folder} to start +
  • + ) + : ( + files.map((file) => { + return ( +
  • + +
  • + ); + }) + )} +
+
+ ); +}; diff --git a/src/components/FileTree/FileFolder.tsx b/src/components/FileTree/FileFolder.tsx deleted file mode 100644 index 57deaee..0000000 --- a/src/components/FileTree/FileFolder.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { assets } from "@kaplayjs/crew"; -import { type FC, type PropsWithChildren, useState } from "react"; -import { cn } from "../../util/cn"; -import FileToolbar from "./FileToolbar"; -import "./FileFolder.css"; - -type Props = PropsWithChildren<{ - level: 0 | 1 | 2; - title?: string; - toolbar?: boolean; -}>; - -const paddingLevels = { - 0: "pl-0", - 1: "pl-4", - 2: "pl-8", -}; - -const FileFolder: FC = ( - { level, title, toolbar = true, children }, -) => { - const [folded, setFolded] = useState(false); - - return ( -
-
- {title &&

{title}

} - {toolbar && ( - - - - )} -
- -
    - {children} -
-
- ); -}; - -export default FileFolder; diff --git a/src/components/FileTree/FileToolbar.tsx b/src/components/FileTree/FileToolbar.tsx index e0e4e9e..180cf7f 100644 --- a/src/components/FileTree/FileToolbar.tsx +++ b/src/components/FileTree/FileToolbar.tsx @@ -1,20 +1,26 @@ import { assets } from "@kaplayjs/crew"; import type { FC, PropsWithChildren } from "react"; import { useProject } from "../../hooks/useProject"; +import { type FileKind, folderByKind } from "../../stores/storage/files"; -const FileToolbar: FC = ({ children }) => { - const { addFile } = useProject(); +type Props = PropsWithChildren<{ + kind: FileKind; +}>; + +export const FileToolbar: FC = (props) => { + const { addFile, getFile } = useProject(); const handleAddFile = () => { - const sceneName = prompt("Scene name"); - if (!sceneName) return; + const fileName = prompt("File name"); + if (!fileName) return; + if (getFile(`${folderByKind[props.kind]}/${fileName}.js`)) return; addFile({ - name: sceneName + ".js", + name: fileName + ".js", kind: "scene", - value: `scene("${sceneName}", () => {\n\n});`, + value: `scene("${fileName}", () => {\n\n});`, language: "javascript", - path: `scenes/${sceneName}.js`, + path: `${folderByKind[props.kind]}/${fileName}.js`, }); }; @@ -31,9 +37,7 @@ const FileToolbar: FC = ({ children }) => { /> - {children} + {props.children} ); }; - -export default FileToolbar; diff --git a/src/components/FileTree/FileTree.tsx b/src/components/FileTree/FileTree.tsx index a7b6a7e..f43262a 100644 --- a/src/components/FileTree/FileTree.tsx +++ b/src/components/FileTree/FileTree.tsx @@ -1,48 +1,20 @@ -import { useProject } from "../../hooks/useProject"; -import FileEntry from "./FileEntry"; -import FileFolder from "./FileFolder"; +import { View } from "../UI/View"; +import { FileFold } from "./FileFold"; export const FileTree = () => { - const { - getFile, - getFilesByFolder, - getProject, - } = useProject(); - return ( -
- {getProject().mode === "pj" && ( - <> - - {getFilesByFolder("scenes").length === 0 - ? ( -
  • - No scenes yet -
  • - ) - : ( - getFilesByFolder("scenes").map((file) => { - return ( -
  • - -
  • - ); - }) - )} -
    - -
  • - <> - - - - -
  • -
    - - )} -
    + + + + ); }; diff --git a/src/components/Playground/WorkspaceExample.tsx b/src/components/Playground/WorkspaceExample.tsx index eac1fce..df6fd55 100644 --- a/src/components/Playground/WorkspaceExample.tsx +++ b/src/components/Playground/WorkspaceExample.tsx @@ -2,6 +2,7 @@ import { Allotment } from "allotment"; import type { FC } from "react"; import { cn } from "../../util/cn"; import { MonacoEditor } from "../Editor/MonacoEditor"; +import { Toolbar } from "../Toolbar"; import ExampleList from "../Toolbar/ExampleList"; import ToolbarToolsMenu from "../Toolbar/ToolbarToolsMenu"; import { GameView } from "./GameView"; @@ -21,7 +22,7 @@ export const WorkspaceExample: FC = (props) => { })} >
    - + {props.isPortrait && || }
    diff --git a/src/components/Toolbar/ToolbarToolsMenu.tsx b/src/components/Toolbar/ToolbarToolsMenu.tsx index 2db0a47..48b67dc 100644 --- a/src/components/Toolbar/ToolbarToolsMenu.tsx +++ b/src/components/Toolbar/ToolbarToolsMenu.tsx @@ -31,7 +31,7 @@ const ToolbarToolsMenu: FC = () => { }; return ( -
      +
        ; + +export const View: FC = (props) => { + const needsFlex = props.direction || props.gap; + + return ( +
        + {props.children} +
        + ); +}; diff --git a/src/stores/storage/files.ts b/src/stores/storage/files.ts index be95c7b..8767ecc 100644 --- a/src/stores/storage/files.ts +++ b/src/stores/storage/files.ts @@ -4,6 +4,7 @@ import type { KAPLAYConfigSlice } from "../kaplayConfig"; import type { ProjectSlice } from "../project"; export type FileKind = "kaplay" | "main" | "scene" | "assets"; +export type FileFolder = "scenes" | "assets" | "root"; export type File = { name: string; @@ -13,6 +14,13 @@ export type File = { kind: FileKind; }; +export const folderByKind: Record = { + kaplay: "root", + main: "root", + scene: "scenes", + assets: "assets", +}; + export interface FilesSlice { /** Add a file. */ addFile: (file: File) => void; @@ -31,7 +39,7 @@ export interface FilesSlice { /** Get a file */ getFile: (path: string) => File | null; /** Get files by folder */ - getFilesByFolder: (folder: string) => File[]; + getFilesByFolder: (folder: FileFolder) => File[]; } export const wrapKAPLAYConfig = (config: string) => @@ -125,6 +133,12 @@ export const createFilesSlice: StateCreator< }, getFilesByFolder(folder) { + if (folder === "root") { + return Array.from(get().project.files.values()).filter( + (file) => !file.path.startsWith("scenes"), + ); + } + return Array.from(get().project.files.values()).filter( (file) => file.path.startsWith(folder), );