This commit is contained in:
Rizky 2023-10-25 09:29:24 +07:00
parent 5f1ec15f0d
commit 98bce81b29
12 changed files with 195 additions and 57 deletions

View File

@ -4,7 +4,7 @@ import { EDGlobal } from "./logic/ed-global";
import { edInit } from "./logic/ed-init";
import { edRoute } from "./logic/ed-route";
import { EdMain } from "./panel/main/main";
import { EdTree } from "./panel/tree/tree";
import { EdLeft } from "./ed-left";
import { edUndoManager } from "./logic/ed-undo";
import { EdPopCompGroup } from "./panel/popup/comp-group";
import { EdPaneResize } from "./panel/main/pane-resize";
@ -34,19 +34,16 @@ export const EdBase = () => {
<div className="flex flex-col flex-1">
<div className="flex justify-between"></div>
<div className="flex flex-1 items-stretch">
<EdTree />
<EdLeft />
<EdPaneResize
minSize={200}
size={p.ui.layout.left}
onResize={(size) => {
if (size > 100) {
p.ui.layout.left = size;
p.render();
}
p.ui.layout.left = size;
p.render();
}}
onDone={(size) => {
if (size > 100) {
localStorage.setItem("prasi-layout-left", size.toString());
}
localStorage.setItem("prasi-layout-left", size.toString());
}}
/>
<EdMain />

View File

@ -1,11 +1,15 @@
import { MultiBackend, getBackendOptions } from "@minoru/react-dnd-treeview";
import { DndProvider } from "react-dnd";
import { EdTreeBody } from "./body";
import { EdTreeSearch } from "./search";
import { useGlobal } from "web-utils";
import { EDGlobal } from "../../logic/ed-global";
import { EDGlobal } from "./logic/ed-global";
import { EdTreeBody } from "./panel/tree/body";
import { EdTreeSearch } from "./panel/tree/search";
import { EdSitePicker } from "./panel/top/left/site";
import { EdApiConfig } from "./panel/top/left/api";
import { EdExport } from "./panel/top/left/export";
import { EdNpmConfig } from "./panel/top/left/npm";
export const EdTree = () => {
export const EdLeft = () => {
const p = useGlobal(EDGlobal, "EDITOR");
return (
<div
@ -17,6 +21,19 @@ export const EdTree = () => {
)}
>
<div className="absolute inset-0 flex flex-col overflow-hidden">
<div
className={cx(
"h-[35px] border-b flex p-1 items-stretch text-[12px] justify-between"
)}
>
<EdSitePicker />
<div className="flex items-stretch space-x-1 pl-2">
<EdNpmConfig />
<EdApiConfig />
<EdExport />
</div>
</div>
<EdTreeSearch />
<div className="tree-body flex relative flex-1 overflow-y-auto overflow-x-hidden">
<div className="absolute inset-0 flex">

View File

@ -77,13 +77,13 @@ export const EDGlobal = {
entry: [] as string[],
tree: [] as NodeModel<EdMeta>[],
meta: {} as Record<string, EdMeta>,
list: {} as Record<string, EPage>,
list: {} as Record<string, { page: EPage; doc: DPage }>,
},
comp: {
cur: EmptyComp,
doc: null as null | DComp,
item: null as null | IItem,
list: {} as Record<string, { cur: EComp; doc: DComp }>,
list: {} as Record<string, { comp: EComp; doc: DComp }>,
group: {} as Record<string, Awaited<ReturnType<SAction["comp"]["group"]>>>,
},
ui: {

View File

@ -16,55 +16,69 @@ export const edRoute = async (p: PG) => {
p.site = site;
}
if (p.page.cur.id !== params.page_id || !p.page.cur.snapshot) {
p.status = "loading";
const page = await p.sync.page.load(params.page_id);
if (
p.page.cur.id !== params.page_id ||
!p.page.cur.snapshot ||
!p.page.list[p.page.cur.id]
) {
if (p.page.cur.snapshot && p.page.list[p.page.cur.id]) {
console.log("loading page from cache");
} else {
p.status = "loading";
const page = await p.sync.page.load(params.page_id);
if (!page) {
p.status = "page-not-found";
p.render();
return;
}
if (!page) {
p.status = "page-not-found";
p.render();
return;
}
p.page.cur = page;
if (page.snapshot) {
const doc = new Y.Doc();
Y.applyUpdate(doc, decompress(page.snapshot));
doc.on("update", async (bin: Uint8Array, origin: any) => {
if (origin === "sv_remote" || origin === "local") return;
p.page.cur = page;
if (page.snapshot) {
const doc = new Y.Doc();
Y.applyUpdate(doc, decompress(page.snapshot));
doc.on("update", async (bin: Uint8Array, origin: any) => {
if (origin === "sv_remote" || origin === "local") return;
const res = await p.sync.yjs.sv_local(
"page",
p.page.cur.id,
Buffer.from(compress(bin))
);
if (res) {
const diff_local = Y.encodeStateAsUpdate(
doc as any,
decompress(res.sv)
);
Y.applyUpdate(doc as any, decompress(res.diff), "local");
await treeRebuild(p);
await p.sync.yjs.diff_local(
const res = await p.sync.yjs.sv_local(
"page",
p.page.cur.id,
Buffer.from(compress(diff_local))
Buffer.from(compress(bin))
);
p.ui.syncing = false;
p.render();
if (res) {
const diff_local = Y.encodeStateAsUpdate(
doc as any,
decompress(res.sv)
);
Y.applyUpdate(doc as any, decompress(res.diff), "local");
await treeRebuild(p);
await p.sync.yjs.diff_local(
"page",
p.page.cur.id,
Buffer.from(compress(diff_local))
);
p.ui.syncing = false;
p.render();
}
});
p.page.doc = doc as any;
if (p.page.doc) {
p.page.list[page.id] = {
page: p.page.cur,
doc: p.page.doc,
};
}
});
p.page.doc = doc as any;
if (p.page.doc) {
await treeRebuild(p);
if (p.page.doc) {
await treeRebuild(p);
}
}
p.status = "ready";
p.render();
}
p.status = "ready";
p.render();
}
}
};

View File

@ -306,7 +306,7 @@ const loadComponent = async (p: PG, item_comp: FNComponent) => {
const doc = new Y.Doc() as DComp;
if (cur.snapshot) {
Y.applyUpdate(doc as any, decompress(cur.snapshot));
p.comp.list[item_comp.id] = { cur, doc };
p.comp.list[item_comp.id] = { comp: cur, doc };
return true;
}
}

View File

@ -1,6 +1,7 @@
import { useLocal } from "web-utils";
export const EdPaneResize = (arg: {
minSize: number;
size: number;
onResize: (size: number) => void;
onDone: (size: number) => void;
@ -29,7 +30,10 @@ export const EdPaneResize = (arg: {
local.inzone = true;
}}
onPointerMove={(e) => {
local.result = Math.max(0, local.size + e.clientX - local.sx);
local.result = Math.max(
arg.minSize,
local.size + e.clientX - local.sx
);
arg.onResize(local.result);
}}
onPointerUp={stopDrag}

View File

@ -0,0 +1,5 @@
import { TopBtn } from "../top-btn";
export const EdApiConfig = () => {
return <TopBtn className="font-bold font-mono text-[10px]">API</TopBtn>;
};

View File

@ -0,0 +1,22 @@
import { TopBtn } from "../top-btn";
export const EdExport = () => {
return (
<TopBtn>
<svg
xmlns="http://www.w3.org/2000/svg"
width="15"
height="15"
fill="none"
viewBox="0 0 15 15"
>
<path
fill="currentColor"
fillRule="evenodd"
d="M6.854 3.854l.8-.8c.644-.645 1.775-1.092 2.898-1.253a5.342 5.342 0 011.504-.02c.443.066.714.196.84.323.127.126.257.397.323.84.064.427.059.95-.02 1.504-.16 1.123-.608 2.254-1.253 2.898L7.5 11.793l-1.146-1.146a.5.5 0 10-.708.707l1.5 1.5a.5.5 0 00.708 0l.547-.548 1.17 1.951a.5.5 0 00.783.097l2-2a.5.5 0 00.141-.425l-.465-3.252.624-.623c.855-.856 1.358-2.225 1.535-3.465.09-.627.1-1.25.019-1.794-.08-.528-.256-1.05-.604-1.399-.349-.348-.871-.525-1.4-.604a6.333 6.333 0 00-1.793.02C9.17.987 7.8 1.49 6.946 2.345l-.623.624-3.252-.465a.5.5 0 00-.425.141l-2 2a.5.5 0 00.097.783l1.95 1.17-.547.547a.5.5 0 000 .708l1.5 1.5a.5.5 0 10.708-.708L3.207 7.5l.647-.646 3-3zm3.245 9.34l-.97-1.617 2.017-2.016.324 2.262-1.37 1.37zM3.423 5.87l2.016-2.016-2.262-.324-1.37 1.37 1.616.97zm-1.07 4.484a.5.5 0 10-.707-.708l-1 1a.5.5 0 10.708.707l1-1zm1.5 1.5a.5.5 0 10-.707-.707l-2 2a.5.5 0 00.708.707l2-2zm1.5 1.5a.5.5 0 10-.707-.708l-1 1a.5.5 0 10.708.707l1-1zM9.5 6.748a1.249 1.249 0 100-2.498 1.249 1.249 0 000 2.498z"
clipRule="evenodd"
></path>
</svg>
</TopBtn>
);
};

View File

@ -0,0 +1,19 @@
import { TopBtn } from "../top-btn";
export const EdNpmConfig = () => {
return (
<TopBtn>
<svg
xmlns="http://www.w3.org/2000/svg"
width="26"
height="26"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M4 10v4h2v-3h1v3h1v-4H4m5 0v5h2v-1h2v-4H9m3 1v2h-1v-2h1m2-1v4h2v-3h1v3h1v-3h1v3h1v-4h-6M3 9h18v6h-9v1H8v-1H3V9z"
></path>
</svg>
</TopBtn>
);
};

View File

@ -0,0 +1,19 @@
import { useGlobal } from "web-utils";
import { EDGlobal } from "../../../logic/ed-global";
import { TopBtn } from "../top-btn";
export const EdSitePicker = () => {
const p = useGlobal(EDGlobal, "EDITOR");
return (
<TopBtn>
<div
dangerouslySetInnerHTML={{
__html: `<svg width="11" height="11" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2.5 3C2.22386 3 2 3.22386 2 3.5V9.5C2 9.77614 2.22386 10 2.5 10H12.5C12.7761 10 13 9.77614 13 9.5V3.5C13 3.22386 12.7761 3 12.5 3H2.5ZM1 9.5C1 10.1531 1.4174 10.7087 2 10.9146V11.5C2 12.3284 2.67157 13 3.5 13H11.5C12.3284 13 13 12.3284 13 11.5V10.9146C13.5826 10.7087 14 10.1531 14 9.5V3.5C14 2.67157 13.3284 2 12.5 2H2.5C1.67157 2 1 2.67157 1 3.5V9.5ZM12 11.5V11H3V11.5C3 11.7761 3.22386 12 3.5 12H11.5C11.7761 12 12 11.7761 12 11.5ZM5.5 6C5.22386 6 5 6.22386 5 6.5C5 6.77614 5.22386 7 5.5 7H9.5C9.77614 7 10 6.77614 10 6.5C10 6.22386 9.77614 6 9.5 6H5.5Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
}}
></div>
<div className="overflow-hidden text-ellipsis max-w-[70px] whitespace-nowrap">
{p.site.name}
</div>
</TopBtn>
);
};

View File

@ -0,0 +1,41 @@
import { ReactNode } from "react";
export const TopBtn = ({
children,
className,
disabled,
underlight,
}: {
children: ReactNode;
className?: string;
disabled?: boolean;
underlight?: string;
}) => {
return (
<div
className={cx(
"px-2 flex items-center cursor-pointer space-x-1 select-none relative",
!disabled &&
"border border-slate-300 hover:bg-blue-500 hover:border-blue-500 hover:text-white transition-all duration-200 rounded-[2px]",
disabled && "text-slate-400 border border-slate-100",
underlight &&
css`
border-bottom-color: ${underlight};
`,
className
)}
>
{underlight && (
<div
className={cx(
"absolute bottom-0 left-[-1px] right-[-1px] h-[3px]",
css`
background: ${underlight};
`
)}
></div>
)}
{children}
</div>
);
};

View File

@ -220,7 +220,7 @@ export const nodeRender: NodeRender<EdMeta> = (node, prm) => {
return;
}
if (e.key.length === 1) {
if (e.key.length === 1 && !e.altKey && !e.metaKey && !e.shiftKey) {
p.ui.tree.search_ref?.focus();
}
}}