checkpoint

This commit is contained in:
Rizky 2023-10-23 08:55:00 +07:00
parent 0a789d9b02
commit 030abd91b0
23 changed files with 313 additions and 28 deletions

View File

@ -5,28 +5,36 @@ export const SyncActionDefinition = {
"load": "2" "load": "2"
}, },
"comp": { "comp": {
"list": "3", "undo": "3",
"group": "4", "redo": "4",
"load": "5" "list": "5",
}, "group": "6",
"page": {
"list": "6",
"load": "7" "load": "7"
}, },
"page": {
"undo": "8",
"redo": "9",
"list": "10",
"load": "11"
},
"yjs": { "yjs": {
"sv_local": "8", "sv_local": "12",
"diff_local": "9" "diff_local": "13"
} }
}; };
export const SyncActionPaths = { export const SyncActionPaths = {
"0": "site.list", "0": "site.list",
"1": "site.group", "1": "site.group",
"2": "site.load", "2": "site.load",
"3": "comp.list", "3": "comp.undo",
"4": "comp.group", "4": "comp.redo",
"5": "comp.load", "5": "comp.list",
"6": "page.list", "6": "comp.group",
"7": "page.load", "7": "comp.load",
"8": "yjs.sv_local", "8": "page.undo",
"9": "yjs.diff_local" "9": "page.redo",
"10": "page.list",
"11": "page.load",
"12": "yjs.sv_local",
"13": "yjs.diff_local"
}; };

View File

@ -22,11 +22,15 @@ export const SyncActions = {
load: async (id: string) => ({}) as ESite | void, load: async (id: string) => ({}) as ESite | void,
}, },
comp: { comp: {
undo: async (id_comp: string) => {},
redo: async (id_comp: string) => {},
list: () => ({}) as Record<string, Exclude<component, "content_tree">>, list: () => ({}) as Record<string, Exclude<component, "content_tree">>,
group: () => ({}) as Record<string, string[]>, group: () => ({}) as Record<string, string[]>,
load: async (id: string) => ({}) as EComp | void, load: async (id: string) => ({}) as EComp | void,
}, },
page: { page: {
undo: async (id_page: string) => {},
redo: async (id_page: string) => {},
list: (id_site: string) => list: (id_site: string) =>
({}) as Record<string, Exclude<page, "content_tree">>, ({}) as Record<string, Exclude<page, "content_tree">>,
load: async (id: string) => ({}) as EPage | void, load: async (id: string) => ({}) as EPage | void,

View File

@ -0,0 +1,3 @@
import { ActionCtx } from "../type";
export const comp_redo = async function (this: ActionCtx, id: string) {};

View File

@ -0,0 +1,3 @@
import { ActionCtx } from "../type";
export const comp_undo = async function (this: ActionCtx, id: string) {};

View File

@ -2,5 +2,9 @@ export * from "./site_load";
export * from "./site_group"; export * from "./site_group";
export * from "./page_load"; export * from "./page_load";
export * from "./comp_load"; export * from "./comp_load";
export * from "./page_undo";
export * from "./page_redo";
export * from "./comp_undo";
export * from "./comp_redo";
export * from "./yjs_sv_local"; export * from "./yjs_sv_local";
export * from "./yjs_diff_local"; export * from "./yjs_diff_local";

View File

@ -11,7 +11,7 @@ export const page_load: SAction["page"]["load"] = async function (
) { ) {
let snap = snapshot.get("page", id); let snap = snapshot.get("page", id);
let ydoc = docs.page[id]; let ydoc = docs.page[id];
if (!snap && !ydoc) { if (!snap && !ydoc) {
const page = await db.page.findFirst({ where: { id } }); const page = await db.page.findFirst({ where: { id } });
if (page) { if (page) {

View File

@ -0,0 +1,13 @@
import { docs } from "../entity/docs";
import { ActionCtx } from "../type";
export const page_redo = async function (this: ActionCtx, id: string) {
if (!docs.page[id]) {
return;
}
const um = docs.page[id].um;
if (um.canRedo()) {
um.redo();
}
};

View File

@ -0,0 +1,13 @@
import { docs } from "../entity/docs";
import { ActionCtx } from "../type";
export const page_undo = async function (this: ActionCtx, id: string) {
if (!docs.page[id]) {
return;
}
const um = docs.page[id].um;
if (um.canUndo()) {
um.undo();
}
};

View File

@ -5,10 +5,13 @@ import { edInit } from "./logic/ed-init";
import { edRoute } from "./logic/ed-route"; import { edRoute } from "./logic/ed-route";
import { EdMain } from "./panel/main/main"; import { EdMain } from "./panel/main/main";
import { EdTree } from "./panel/tree/tree"; import { EdTree } from "./panel/tree/tree";
import { edUndoManager } from "./logic/ed-undo";
export const EdBase = () => { export const EdBase = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
edUndoManager(p);
if (p.status === "init") { if (p.status === "init") {
edInit(p); edInit(p);
} }

View File

@ -68,11 +68,16 @@ export const EDGlobal = {
item: null as null | IItem, item: null as null | IItem,
list: {} as Record<string, { cur: EComp; doc: DComp }>, list: {} as Record<string, { cur: EComp; doc: DComp }>,
}, },
ui: { ui: {
select: {
id: "",
},
tree: { tree: {
open: {} as Record<string, string[]>, open: {} as Record<string, string[]>,
}, },
popup: {
comp: null as null | ((comp_id: string) => void | Promise<void>),
},
}, },
}; };

View File

@ -0,0 +1,61 @@
import { useEffect } from "react";
import { PG } from "./ed-global";
import { treeRebuild } from "./tree/build";
export const edUndoManager = async (p: PG) => {
useEffect(() => {
const keyDown = async (evt: KeyboardEvent) => {
if (
(evt.key === "s" || evt.key === "s") &&
(evt.ctrlKey || evt.metaKey)
) {
evt.preventDefault();
evt.stopPropagation();
}
if (
(evt.key === "Y" || evt.key === "y") &&
(evt.ctrlKey || evt.metaKey) &&
!evt.shiftKey
) {
console.log("redo");
return;
}
if (
(evt.key === "Z" || evt.key === "z") &&
(evt.ctrlKey || evt.metaKey) &&
evt.shiftKey
) {
console.log("redo");
return;
}
if (
(evt.key === "Z" || evt.key === "z") &&
(evt.ctrlKey || evt.metaKey) &&
!evt.shiftKey
) {
if (p.comp.cur.id) {
p.sync.comp.undo(p.comp.cur.id);
} else {
p.sync.page.undo(p.page.cur.id);
}
}
if (
(evt.key === "r" || evt.key === "R" || evt.key === "®") &&
evt.altKey
) {
evt.preventDefault();
evt.stopPropagation();
await treeRebuild(p, { note: "reload" });
}
};
window.addEventListener("keydown", keyDown, true);
return () => {
window.removeEventListener("keydown", keyDown, true);
};
}, []);
};

View File

@ -14,7 +14,7 @@ import { MSection } from "../../../../utils/types/section";
import { EdMeta, PG } from "../ed-global"; import { EdMeta, PG } from "../ed-global";
import { decompress } from "wasm-gzip"; import { decompress } from "wasm-gzip";
export const treeRebuild = async (p: PG) => { export const treeRebuild = async (p: PG, arg?: { note?: string }) => {
const doc = p.page.doc; const doc = p.page.doc;
if (!doc) return; if (!doc) return;

View File

@ -0,0 +1,12 @@
import { IItem } from "../../../../../../../utils/types/item";
import { MenuItem } from "../../../../../../../utils/ui/context-menu";
import { PG } from "../../../../../logic/ed-global";
export const edActionAttach = (p: PG, item: IItem) => {
p.ui.select.id = item.id;
const pick = () => {
p.ui.popup.comp = (comp_id) => {};
p.render();
};
pick();
};

View File

@ -0,0 +1,4 @@
import { IContent } from "../../../../../../../utils/types/general";
import { PG } from "../../../../../logic/ed-global";
export const edActionClone = (p: PG, item: IContent) => {};

View File

@ -0,0 +1,14 @@
import { IContent } from "../../../../../../../utils/types/general";
import { PG } from "../../../../../logic/ed-global";
export const edActionCopy = async (p: PG, item: IContent) => {
const perm = await navigator.permissions.query({
name: "clipboard-read",
allowWithoutGesture: false,
} as any);
if (perm.state !== "granted") {
await navigator.clipboard.read();
}
let str = `prasi-clipboard:` + JSON.stringify(item);
navigator.clipboard.writeText(str);
};

View File

@ -0,0 +1,25 @@
import { IContent } from "../../../../../../../utils/types/general";
import { PG } from "../../../../../logic/ed-global";
import { treeRebuild } from "../../../../../logic/tree/build";
export const edActionCut = async (p: PG, item: IContent) => {
const perm = await navigator.permissions.query({
name: "clipboard-read",
allowWithoutGesture: false,
} as any);
if (perm.state !== "granted") {
await navigator.clipboard.read();
}
let str = `prasi-clipboard:` + JSON.stringify(item);
navigator.clipboard.writeText(str);
const mitem = p.page.meta[item.id].mitem;
if (mitem) {
mitem.parent.forEach((e, k) => {
if (e == mitem) {
mitem.parent.delete(k);
}
});
await treeRebuild(p);
}
};

View File

@ -0,0 +1,4 @@
import { IItem } from "../../../../../../../utils/types/item";
import { PG } from "../../../../../logic/ed-global";
export const edActionDetach = (p: PG, item: IItem) => {};

View File

@ -0,0 +1,4 @@
import { IContent } from "../../../../../../../utils/types/general";
import { PG } from "../../../../../logic/ed-global";
export const edActionHide = (p: PG, item: IContent) => {};

View File

@ -0,0 +1,4 @@
import { IItem } from "../../../../../../../utils/types/item";
import { PG } from "../../../../../logic/ed-global";
export const edActionNewComp = (p: PG, item: IItem) => {};

View File

@ -0,0 +1,4 @@
import { IContent } from "../../../../../../../utils/types/general";
import { PG } from "../../../../../logic/ed-global";
export const edActionPaste = (p: PG, item: IContent) => {};

View File

@ -1,16 +1,93 @@
import { import { NodeModel, RenderParams } from "@minoru/react-dnd-treeview";
NodeModel, import { useGlobal, useLocal } from "web-utils";
NodeRender, import { IItem } from "../../../../../../utils/types/item";
RenderParams, import { FNComponent } from "../../../../../../utils/types/meta-fn";
} from "@minoru/react-dnd-treeview"; import { Menu, MenuItem } from "../../../../../../utils/ui/context-menu";
import { EdMeta } from "../../../../logic/ed-global"; import { EDGlobal, EdMeta } from "../../../../logic/ed-global";
import { edActionAttach } from "./action/attach";
import { edActionClone } from "./action/clone";
import { edActionCopy } from "./action/copy";
import { edActionCut } from "./action/cut";
import { edActionDetach } from "./action/detach";
import { edActionHide } from "./action/hide";
import { edActionNewComp } from "./action/new-comp";
import { edActionPaste } from "./action/paste";
export const EdTreeCtxMenu = ({ export const EdTreeCtxMenu = ({
node, node,
prm, prm,
event,
onClose,
}: { }: {
event: React.MouseEvent<HTMLDivElement, MouseEvent>;
node: NodeModel<EdMeta>; node: NodeModel<EdMeta>;
prm: RenderParams; prm: RenderParams;
onClose: () => void;
}) => { }) => {
return <></>; const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({ allowCopy: false, allowPaste: false }, async () => {
const permissionStatus = await navigator.permissions.query({
name: "clipboard-read",
allowWithoutGesture: false,
} as any);
if (permissionStatus.state === "granted") {
local.allowCopy = true;
local.render();
navigator.clipboard
.readText()
.then((e) => {
if (e.startsWith("prasi-clipboard:")) {
local.allowPaste = true;
local.render();
}
})
.catch(() => {});
}
});
const item = node.data?.item;
const type = item?.type;
const comp = (item as IItem).component as FNComponent | undefined;
const rootComp = p.comp.cur;
const isActiveComponent = rootComp && rootComp.id === item?.id && rootComp.id;
if (!item) {
return (
<Menu mouseEvent={event} onClose={onClose}>
<MenuItem label={<div className="text-gray-400">Unavailable</div>} />
</Menu>
);
}
return (
<Menu mouseEvent={event} onClose={onClose}>
{type === "item" && !isActiveComponent && !item.component?.id && (
<MenuItem
label="Attach Component"
onClick={() => edActionAttach(p, item)}
/>
)}
{type === "item" && comp?.id && !isActiveComponent && (
<MenuItem
label="Detach Component"
onClick={() => edActionDetach(p, item)}
/>
)}
{type === "item" && comp?.id && (
<MenuItem
label="Create Component"
onClick={() => edActionNewComp(p, item)}
/>
)}
{!item.hidden && (
<MenuItem label="Hide" onClick={() => edActionHide(p, item)} />
)}
<MenuItem label="Clone" onClick={() => edActionClone(p, item)} />
<MenuItem label="Cut" onClick={() => edActionCut(p, item)} />
<MenuItem label="Copy" onClick={() => edActionCopy(p, item)} />
{local.allowCopy && local.allowPaste && (
<MenuItem label="Paste" onClick={() => edActionPaste(p, item)} />
)}
</Menu>
);
}; };

View File

@ -5,19 +5,39 @@ import { EdTreeCtxMenu } from "./item/ctx-menu";
import { EdTreeIndent } from "./item/indent"; import { EdTreeIndent } from "./item/indent";
import { EdTreeName } from "./item/name"; import { EdTreeName } from "./item/name";
import { indentHook } from "./item/indent-hook"; import { indentHook } from "./item/indent-hook";
import { useLocal } from "web-utils";
export const nodeRender: NodeRender<EdMeta> = (node, prm) => { export const nodeRender: NodeRender<EdMeta> = (node, prm) => {
const local = useLocal({
rightClick: null as null | React.MouseEvent<HTMLDivElement, MouseEvent>,
});
if (!node || !node.data) return <></>; if (!node || !node.data) return <></>;
const item = node.data?.item; const item = node.data?.item;
const isComponent = item.type === "item" && item.component?.id; const isComponent = item.type === "item" && item.component?.id;
return ( return (
<div <div
className={cx( className={cx(
"relative border-b flex items-stretch hover:bg-blue-50 min-h-[26px]", "relative border-b flex items-stretch hover:bg-blue-50 min-h-[26px]",
isComponent && `bg-purple-50` isComponent && `bg-purple-50`
)} )}
onContextMenu={(event) => {
event.preventDefault();
local.rightClick = event;
local.render();
}}
> >
<EdTreeCtxMenu node={node} prm={prm} /> {local.rightClick && (
<EdTreeCtxMenu
node={node}
prm={prm}
event={local.rightClick}
onClose={() => {
local.rightClick = null;
local.render();
}}
/>
)}
<EdTreeIndent node={node} prm={prm} /> <EdTreeIndent node={node} prm={prm} />
<EdTreeName node={node} prm={prm} /> <EdTreeName node={node} prm={prm} />
<EdTreeAction node={node} prm={prm} /> <EdTreeAction node={node} prm={prm} />

View File

@ -4,7 +4,7 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bun run --silent --watch ./pkgs/core/index.ts dev", "dev": "bun run --silent --watch ./pkgs/core/index.ts dev",
"clean": "rm -rf app/static && rm -rf app/web/.parcel-cache", "clean": "rm -rf data && rm -rf app/static && rm -rf app/web/.parcel-cache",
"build": "bun run --silent ./pkgs/core/build.ts", "build": "bun run --silent ./pkgs/core/build.ts",
"db-pull": "bun run ./pkgs/core/db-pull.ts", "db-pull": "bun run ./pkgs/core/db-pull.ts",
"parcel": "bun clean && bun run ./pkgs/core/parcel.ts", "parcel": "bun clean && bun run ./pkgs/core/parcel.ts",
@ -13,7 +13,7 @@
"pull": "cd app/db && bun prisma db pull && bun prisma generate", "pull": "cd app/db && bun prisma db pull && bun prisma generate",
"pkgs-upgrade": "bun run --silent ./pkgs/core/upgrade.ts" "pkgs-upgrade": "bun run --silent ./pkgs/core/upgrade.ts"
}, },
"workspaces": [ "workspaces": [
"app/*", "app/*",
"pkgs/*" "pkgs/*"
], ],