This commit is contained in:
Rizky 2024-02-20 18:10:46 +07:00
parent 86ef473710
commit db45f936fc
11 changed files with 218 additions and 31 deletions

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,7 @@
"@monaco-editor/react": "^4.6.0",
"@paralleldrive/cuid2": "2.2.2",
"react-contenteditable": "^3.3.7",
"react-dropzone": "14.2.3",
"@parcel/packager-wasm": "^2.10.3",
"@parcel/service-worker": "^2.10.3",
"recast": "^0.23.4",

View File

@ -46,11 +46,19 @@ export const apiProxy = (api_url: string) => {
}
if (api_ref) {
if (actionName === "_raw") {
const url = `${base_url}${rest.join("")}`;
const result = await fetchSendApi(url, rest);
resolve(result);
return;
}
if (!api_ref.apiEntry) api_ref.apiEntry = {};
if (api_ref.apiEntry && !api_ref.apiEntry[actionName]) {
reject(
`API ${actionName.toString()} not found, existing API: \n - ${Object.keys(
api_ref || {}
api_ref.apiEntry || {}
).join("\n - ")}`
);
return;

View File

@ -29,7 +29,6 @@ export const Root: FC<{}> = ({}) => {
prasiContext.render = local.render;
const Provider = GlobalContext.Provider as FC<{ value: any; children: any }>;
const found = local.router.lookup(location.pathname);
if (found) {

View File

@ -18,6 +18,7 @@ import { EdPopScript } from "./panel/popup/script/pop-script";
import { EdPopSite } from "./panel/popup/site/site-popup";
import { EdPageHistoryMain } from "./panel/main/main-history";
import { jscript } from "../../utils/script/jscript";
import { useEffect } from "react";
export const EdBase = () => {
const p = useGlobal(EDGlobal, "EDITOR");

View File

@ -10,6 +10,7 @@ import { useGlobal } from "web-utils";
import { ResponsiveToggle } from "./panel/header/right/responsive-toggle";
import { EdCompEditable } from "./panel/header/mid/comp-editable";
import { MobileQRButton } from "./panel/side/style/tools/mobile-qr";
import { EdFileBrowser } from "./panel/file/file-browser";
export const EdMid: FC<{}> = () => {
const p = useGlobal(EDGlobal, "EDITORF");
@ -37,6 +38,7 @@ export const EdMid: FC<{}> = () => {
</div>
</div>
<div className="flex items-stretch flex-1 justify-end">
<EdFileBrowser />
{p.site.responsive !== "desktop-only" && <MobileQRButton />}
<label className=" text-slate-400 flex items-center pr-1">
<div className=" px-1"> Zoom</div>

View File

@ -231,7 +231,7 @@ export const EDGlobal = {
open: {} as Record<string, string[]>,
},
popup: {
file: { enabled: false },
file: { enabled: false, open: true },
code: {
init: false,
open: false,

View File

@ -0,0 +1,119 @@
import { useCallback, useEffect } from "react";
import { useDropzone } from "react-dropzone";
import { useGlobal, useLocal } from "web-utils";
import { apiProxy } from "../../../../base/load/api/api-proxy";
import { Modal } from "../../../../utils/ui/modal";
import { EDGlobal } from "../../logic/ed-global";
import { EdFileTree } from "./file-tree";
import { FEntry } from "./type";
export const EdFileBrowser = () => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({
entry: {} as Record<string, FEntry[]>,
});
useEffect(() => {
if (!p.script.api && p.site.config?.api_url) {
p.script.api = apiProxy(p.site.config.api_url);
p.render();
}
p.script.api._raw(`/_file/?dir`).then((e: FEntry[]) => {
if (Array.isArray(e)) {
local.entry = { "/": e };
p.ui.popup.file.enabled = true;
p.render();
}
});
}, []);
const onDrop = useCallback((acceptedFiles: any[]) => {
console.log(acceptedFiles);
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
noClick: true,
});
if (!p.ui.popup.file.enabled) return null;
return (
<>
<div
className="items-center flex px-2 cursor-pointer border border-transparent hover:bg-slate-200 transition-all hover:border-black"
onClick={() => {
p.ui.popup.file.open = true;
p.render();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
>
<path d="M20 10a1 1 0 001-1V6a1 1 0 00-1-1h-2.5a1 1 0 01-.8-.4l-.9-1.2A1 1 0 0015 3h-2a1 1 0 00-1 1v5a1 1 0 001 1zM20 21a1 1 0 001-1v-3a1 1 0 00-1-1h-2.9a1 1 0 01-.88-.55l-.42-.85a1 1 0 00-.92-.6H13a1 1 0 00-1 1v5a1 1 0 001 1zM3 5a2 2 0 002 2h3"></path>
<path d="M3 3v13a2 2 0 002 2h3"></path>
</svg>{" "}
<div className="pl-1">Files</div>
</div>
<Modal
fade={false}
open={p.ui.popup.file.open}
onOpenChange={(open) => {
if (!open) {
p.ui.popup.file.open = false;
p.render();
}
}}
>
<div
{...getRootProps()}
className={cx(
"bg-white select-none fixed inset-[50px] flex",
css`
outline: none;
`
)}
>
<input {...getInputProps()} />
{isDragActive && (
<div className="absolute inset-0 flex items-center justify-center flex-col bg-blue-50 border-4 border-blue-500">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
className="lucide lucide-upload"
viewBox="0 0 24 24"
>
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
<path d="M17 8L12 3 7 8"></path>
<path d="M12 3L12 15"></path>
</svg>
<div>Drag Here to Upload</div>
</div>
)}
<div className="flex flex-1 items-stretch">
<div className="flex min-w-[200px] border-r">
<EdFileTree entry={local.entry} />
</div>
<div>Moko</div>
</div>
</div>
</Modal>
</>
);
};

View File

@ -0,0 +1,52 @@
import {
MultiBackend,
NodeModel,
Tree as DNDTree,
getBackendOptions,
} from "@minoru/react-dnd-treeview";
import { DndProvider } from "react-dnd";
import { FEntry } from "./type";
import { FC } from "react";
const Tree = DNDTree<FEntry>;
export const EdFileTree: FC<{ entry: Record<string, FEntry[]> }> = ({
entry,
}) => {
const tree: NodeModel<FEntry>[] = [];
for (const [path, entries] of Object.entries(entry)) {
const arr = path.split("/");
const name = arr.pop() || "/";
tree.push({ id: path, text: name, parent: arr.join("/") });
for (const e of entries) {
if (e.type === "dir") {
tree.push({
id: (path === "/" ? "" : path) + "/" + e.name,
text: e.name,
parent: path,
});
}
}
}
return (
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
<Tree
tree={tree}
dragPreviewRender={() => <></>}
rootId=""
initialOpen={true}
onDrop={async (newTree, opt) => {}}
render={(node, { depth, isOpen, onToggle, hasChild }) => (
<div
className={cx(css`
padding-left: ${(depth * 10) + 10}px;
`)}
>
{node.text}
</div>
)}
></Tree>
</DndProvider>
);
};

View File

@ -0,0 +1,5 @@
export type FEntry = {
name: string;
size: number;
type: "dir" | "file";
};

BIN
bun.lockb

Binary file not shown.