wip fix component

This commit is contained in:
Rizky 2023-11-28 13:12:28 +07:00
parent 69b24ec979
commit e7d52b8294
10 changed files with 420 additions and 216 deletions

View File

@ -6,5 +6,6 @@
"**/CVS": true, "**/CVS": true,
"**/.DS_Store": true, "**/.DS_Store": true,
"**/Thumbs.db": true "**/Thumbs.db": true
} },
"hide-files.files": []
} }

View File

@ -260,6 +260,11 @@ export const serverWalkMap = (
let id = item.originalId || item.id; let id = item.originalId || item.id;
const pcomp = p.scope_comps[arg.parent_mcomp.id]; const pcomp = p.scope_comps[arg.parent_mcomp.id];
if (!pcomp) {
console.log(arg.parent_mcomp.id);
}
if (pcomp) {
pcomp.scope[id] = { pcomp.scope[id] = {
p: arg.parent_mcomp.parent_ids, p: arg.parent_mcomp.parent_ids,
n: item.name, n: item.name,
@ -282,13 +287,17 @@ export const serverWalkMap = (
parent_mcomp: jsx.parent_mcomp parent_mcomp: jsx.parent_mcomp
? { ? {
...jsx.parent_mcomp, ...jsx.parent_mcomp,
parent_ids: [...(arg.parent_ids || []), mitem.get("id") || ""], parent_ids: [
...(arg.parent_ids || []),
mitem.get("id") || "",
],
} }
: undefined, : undefined,
parent_ids: [...arg.parent_ids, mitem.get("id") || ""], parent_ids: [...arg.parent_ids, mitem.get("id") || ""],
}); });
} }
} }
}
} else { } else {
if (!(item_comp && item_comp.id)) { if (!(item_comp && item_comp.id)) {
p.scope[item.id] = { p: arg.parent_ids, n: item.name, s: null }; p.scope[item.id] = { p: arg.parent_ids, n: item.name, s: null };

View File

@ -241,6 +241,7 @@ export const EDGlobal = {
responsive?: string; responsive?: string;
}, },
comp: { comp: {
preview_id: "",
open: null as null | ((comp_id: string) => void | Promise<void>), open: null as null | ((comp_id: string) => void | Promise<void>),
}, },
comp_group: null as null | { comp_group: null as null | {

View File

@ -21,7 +21,7 @@ export const EdPagePicker = () => {
__html: `<svg width="12" height="12" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 2.5C3 2.22386 3.22386 2 3.5 2H9.08579C9.21839 2 9.34557 2.05268 9.43934 2.14645L11.8536 4.56066C11.9473 4.65443 12 4.78161 12 4.91421V12.5C12 12.7761 11.7761 13 11.5 13H3.5C3.22386 13 3 12.7761 3 12.5V2.5ZM3.5 1C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H11.5C12.3284 14 13 13.3284 13 12.5V4.91421C13 4.51639 12.842 4.13486 12.5607 3.85355L10.1464 1.43934C9.86514 1.15804 9.48361 1 9.08579 1H3.5ZM4.5 4C4.22386 4 4 4.22386 4 4.5C4 4.77614 4.22386 5 4.5 5H7.5C7.77614 5 8 4.77614 8 4.5C8 4.22386 7.77614 4 7.5 4H4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H10.5C10.7761 8 11 7.77614 11 7.5C11 7.22386 10.7761 7 10.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H10.5C10.7761 11 11 10.7761 11 10.5C11 10.2239 10.7761 10 10.5 10H4.5Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`, __html: `<svg width="12" height="12" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 2.5C3 2.22386 3.22386 2 3.5 2H9.08579C9.21839 2 9.34557 2.05268 9.43934 2.14645L11.8536 4.56066C11.9473 4.65443 12 4.78161 12 4.91421V12.5C12 12.7761 11.7761 13 11.5 13H3.5C3.22386 13 3 12.7761 3 12.5V2.5ZM3.5 1C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H11.5C12.3284 14 13 13.3284 13 12.5V4.91421C13 4.51639 12.842 4.13486 12.5607 3.85355L10.1464 1.43934C9.86514 1.15804 9.48361 1 9.08579 1H3.5ZM4.5 4C4.22386 4 4 4.22386 4 4.5C4 4.77614 4.22386 5 4.5 5H7.5C7.77614 5 8 4.77614 8 4.5C8 4.22386 7.77614 4 7.5 4H4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H10.5C10.7761 8 11 7.77614 11 7.5C11 7.22386 10.7761 7 10.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H10.5C10.7761 11 11 10.7761 11 10.5C11 10.2239 10.7761 10 10.5 10H4.5Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
}} }}
></div> ></div>
<div className="overflow-hidden whitespace-nowrap text-[11px] text-ellipsis max-w-[100px]"> <div className="overflow-hidden whitespace-nowrap text-[12px] text-ellipsis max-w-[100px]">
{p.page.cur.name} {p.page.cur.name}
</div> </div>
</TopBtn> </TopBtn>

View File

@ -4,14 +4,15 @@ import {
TreeMethods, TreeMethods,
getBackendOptions, getBackendOptions,
} from "@minoru/react-dnd-treeview"; } from "@minoru/react-dnd-treeview";
import { useEffect } from "react";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
import { useEffect } from "react";
import { useGlobal, useLocal } from "web-utils"; import { useGlobal, useLocal } from "web-utils";
import { Loading } from "../../../../../utils/ui/loading"; import { Loading } from "../../../../../utils/ui/loading";
import { Modal } from "../../../../../utils/ui/modal"; import { Modal } from "../../../../../utils/ui/modal";
import { EDGlobal } from "../../../logic/ed-global"; import { EDGlobal, active } from "../../../logic/ed-global";
import { compPicker, reloadCompPicker } from "./comp-reload"; import { compPicker, reloadCompPicker } from "./comp-reload";
import { CompItem, edPageTreeRender } from "./comp-tree"; import { CompItem, edPageTreeRender } from "./comp-tree";
import { EdCompPreview } from "./comp-preview";
export const EdPopComp = () => { export const EdPopComp = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
@ -27,6 +28,10 @@ export const EdPopComp = () => {
if (!p.ui.popup.comp.open) return null; if (!p.ui.popup.comp.open) return null;
if (!compPicker.active_id && active.item_id) {
compPicker.active_id = active.item_id;
}
if (p.site.id !== compPicker.site_id) { if (p.site.id !== compPicker.site_id) {
compPicker.site_id = p.site.id; compPicker.site_id = p.site.id;
reloadCompPicker(p); reloadCompPicker(p);
@ -42,6 +47,7 @@ export const EdPopComp = () => {
p.render(); p.render();
} }
}} }}
fade={false}
> >
<div <div
id="comp-picker" id="comp-picker"
@ -52,12 +58,27 @@ export const EdPopComp = () => {
}} }}
className={cx("absolute inset-[5%] bg-white flex")} className={cx("absolute inset-[5%] bg-white flex")}
> >
<div className="relative flex flex-1 items-stretch text-[12px] overflow-auto"> <div className="relative flex flex-1 items-stretch text-[14px] overflow-auto">
{compPicker.status === "loading" && ( {compPicker.status === "loading" && (
<Loading note="listing-comp" backdrop={false} /> <Loading note="listing-comp" backdrop={false} />
)} )}
{compPicker.status !== "loading" && (
{compPicker.ref && ( <>
<div className="flex flex-1 relative overflow-auto">
<div
className={cx(
"absolute inset-0",
css`
> .tree-root > .listitem > .container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
position: relative;
}
`
)}
>
{compPicker.ref && compPicker.status === "ready" && (
<DndProvider <DndProvider
backend={HTML5Backend} backend={HTML5Backend}
options={getBackendOptions({ options={getBackendOptions({
@ -73,17 +94,27 @@ export const EdPopComp = () => {
} }
}} }}
tree={compPicker.tree} tree={compPicker.tree}
initialOpen={true}
rootId={"comp-root"} rootId={"comp-root"}
onDrop={() => {}} onDrop={() => {}}
dragPreviewRender={() => <></>} dragPreviewRender={() => <></>}
canDrag={() => true} canDrag={() => true}
classes={{ root: "flex-1" }} classes={{
root: "tree-root flex-1",
listItem: "listitem",
container: "container",
}}
render={edPageTreeRender} render={edPageTreeRender}
/> />
</DndProvider> </DndProvider>
)} )}
</div> </div>
</div> </div>
<EdCompPreview />
</>
)}
</div>
</div>
</Modal> </Modal>
</> </>
); );

View File

@ -0,0 +1,67 @@
import {
Tree as DNDTree,
DndProvider,
NodeModel,
getBackendOptions,
} from "@minoru/react-dnd-treeview";
import { FC, useEffect } from "react";
import { HTML5Backend } from "react-dnd-html5-backend";
import { EDGlobal, EdMeta } from "../../../logic/ed-global";
import { compPicker } from "./comp-reload";
import { nodeRender } from "../../tree/node/render";
import { useGlobal, useLocal } from "web-utils";
export const EdCompPreviewTree: FC<{ tree: NodeModel<EdMeta>[] }> = ({
tree,
}) => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({ ref: null as any });
const TypedTree = DNDTree<EdMeta>;
useEffect(() => {
if (local.ref) local.ref.openAll();
}, [p.ui.popup.comp.preview_id]);
return (
<div
className="flex flex-col min-w-[200px] overflow-auto relative border-r"
ref={(ref) => {
if (!compPicker.preview_ref) {
setTimeout(p.render, 100);
}
if (ref) compPicker.preview_ref = ref;
}}
>
<div className="absolute inset-0">
{compPicker.preview_ref && (
<DndProvider
backend={HTML5Backend}
options={getBackendOptions({
html5: {
rootElement: compPicker.preview_ref,
},
})}
>
<TypedTree
tree={tree}
ref={(ref) => {
if (ref) local.ref = ref;
}}
initialOpen={true}
rootId={"root"}
onDrop={() => {}}
dragPreviewRender={() => <></>}
canDrag={() => false}
classes={{
root: "tree-root flex-1 text-xs",
listItem: "listitem",
container: "container",
}}
render={nodeRender}
/>
</DndProvider>
)}
</div>
</div>
);
};

View File

@ -0,0 +1,156 @@
import { FC, useEffect } from "react";
import { useGlobal } from "web-utils";
import { produceCSS } from "../../../../../utils/css/gen";
import { IItem } from "../../../../../utils/types/item";
import { IText } from "../../../../../utils/types/text";
import { EDGlobal, PG, active } from "../../../logic/ed-global";
import { loadComponent } from "../../../logic/tree/sync-walk";
import { EdCompPreviewTree } from "./comp-preview-tree";
import { compPicker } from "./comp-reload";
export const EdCompPreview = () => {
const p = useGlobal(EDGlobal, "EDITOR");
const comp_id = p.ui.popup.comp.preview_id;
const ref = p.comp.list[comp_id];
const item = ref?.doc?.getMap("map").get("root")?.toJSON() as
| IItem
| undefined;
useEffect(() => {
if (!p.comp.list[comp_id] && !!comp_id) {
loadComponent(p, comp_id).then(() => {
p.render();
});
}
}, [comp_id]);
return (
<div className="flex flex-1 flex-col items-stretch overflow-auto border-l">
{comp_id && item && (
<div className="flex px-1 py-1 border-b">
<div className="flex flex-1 items-center">
<div>Preview</div>
<div className="text-[8px] font-mono text-slate-500 mx-1">
{comp_id}
</div>
</div>
<div className="flex flex-1 justify-end">
<div
className="cursor-pointer transition-all flex items-center border px-1 border-green-700 bg-green-700 text-white hover:opacity-50 mr-1"
onClick={async (e) => {
e.stopPropagation();
if (p.ui.popup.comp.open) {
p.ui.popup.comp.open(item.id);
}
p.ui.popup.comp.open = null;
active.item_id = compPicker.active_id;
compPicker.active_id = "";
p.render();
}}
>
Select Component
</div>
<div
className="cursor-pointer transition-all bg-white flex items-center border px-1 hover:border-red-300 hover:bg-red-100"
onClick={async (e) => {
e.stopPropagation();
if (confirm("Are you sure to delete this component?")) {
await db.component.delete({
where: { id: p.ui.popup.comp.preview_id },
});
const idx =
compPicker.tree.findIndex((e) => e.id === comp_id) + 1;
if (idx >= 0 && compPicker.tree[idx])
p.ui.popup.comp.preview_id = compPicker.tree[idx].id as any;
compPicker.tree = compPicker.tree.filter(
(e) => e.id !== comp_id
);
p.render();
}
}}
>
<DeleteIcon />
</div>
</div>
</div>
)}
<div className="relative flex flex-1">
<div className="absolute inset-0 flex items-center justify-center">
{comp_id ? (
<>
{item && ref ? (
<div className="flex-1 flex relative w-full h-full">
<EdCompPreviewTree tree={ref.tree} />
<div
className={cx(
"flex-1 flex flex-col relative",
css`
content: contain;
`
)}
>
<CItem p={p} item={item} />
</div>
</div>
) : (
<div>Loading...</div>
)}
</>
) : (
<>
Select component
<br /> to preview
</>
)}
</div>
</div>
</div>
);
};
const CItem: FC<{ item: IItem | IText; p: PG }> = ({ item, p }) => {
const className = produceCSS(item, {
mode: p.mode,
});
if (item.type === "item") {
<div className={className}>
{item.type === "item" &&
item.childs.map((e) => {
return <CItem key={e.id} item={e} p={p} />;
})}
</div>;
}
return (
<div
className={className}
dangerouslySetInnerHTML={
item.type === "text" ? { __html: item.html } : undefined
}
/>
);
};
const DeleteIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
fill="none"
viewBox="0 0 15 15"
>
<path
fill="currentColor"
fillRule="evenodd"
d="M5.5 1a.5.5 0 000 1h4a.5.5 0 000-1h-4zM3 3.5a.5.5 0 01.5-.5h8a.5.5 0 010 1H11v8a1 1 0 01-1 1H5a1 1 0 01-1-1V4h-.5a.5.5 0 01-.5-.5zM5 4h5v8H5V4z"
clipRule="evenodd"
></path>
</svg>
);

View File

@ -5,10 +5,12 @@ import { CompItem } from "./comp-tree";
export const compPicker = { export const compPicker = {
site_id: "", site_id: "",
ref: null as any, ref: null as any,
preview_ref: null as any,
tab: "all" as "all" | "trash", tab: "all" as "all" | "trash",
tree: [] as NodeModel<CompItem>[], tree: [] as NodeModel<CompItem>[],
trash: [] as NodeModel<CompItem>[], trash: [] as NodeModel<CompItem>[],
status: "ready" as "loading" | "ready", status: "ready" as "loading" | "ready",
active_id: "",
render: () => {}, render: () => {},
}; };
@ -61,8 +63,6 @@ export const reloadCompPicker = async (p: PG) => {
} }
} }
console.log(compPicker.tree);
compPicker.status = "ready"; compPicker.status = "ready";
compPicker.render(); compPicker.render();
}; };

View File

@ -1,7 +1,7 @@
import { NodeModel, NodeRender } from "@minoru/react-dnd-treeview"; import { NodeModel, NodeRender } from "@minoru/react-dnd-treeview";
import { FC } from "react"; import { FC } from "react";
import { useGlobal, useLocal } from "web-utils"; import { useGlobal, useLocal } from "web-utils";
import { EDGlobal } from "../../../logic/ed-global"; import { EDGlobal, active } from "../../../logic/ed-global";
import { compPicker, reloadCompPicker } from "./comp-reload"; import { compPicker, reloadCompPicker } from "./comp-reload";
export type CompItem = { export type CompItem = {
@ -21,7 +21,7 @@ export const edPageTreeRender: NodeRender<CompItem> = (
return ( return (
<div <div
className={cx( className={cx(
"flex border-b py-[2px] items-center hover:bg-blue-50 cursor-pointer relative", "flex hover:bg-blue-50 cursor-pointer",
css` css`
.btn { .btn {
opacity: 0; opacity: 0;
@ -30,26 +30,31 @@ export const edPageTreeRender: NodeRender<CompItem> = (
opacity: 1; opacity: 1;
} }
`, `,
item.id === p.page.cur.id && `bg-blue-50` item.id === p.page.cur.id && `bg-blue-50`,
item.type === "component" && "m-1 border flex-1",
item.type === "folder" && "border-t py-[2px] items-center",
item.id === p.ui.popup.comp.preview_id &&
css`
border: 1px solid blue !important;
`
)} )}
onClick={() => { onClick={() => {
if (item.type === "folder") { if (item.type === "folder") {
onToggle(); onToggle();
} else if (p.ui.popup.page.open) { } else {
p.ui.popup.page.open(item.id); p.ui.popup.comp.preview_id = item.id;
p.render();
} }
}} }}
> >
<div className="flex w-[40%] items-center relative ">
{item.id === p.page.cur.id && ( {item.id === p.page.cur.id && (
<div className="absolute left-0 top-0 bottom-0 bg-blue-500 w-1"></div> <div className="absolute left-0 top-0 bottom-0 bg-blue-500 w-1"></div>
)} )}
<div <div
className={cx( className={cx(
"h-[13px] pl-1", "h-[13px]",
css` item.type === "folder" && "pl-1",
width: ${depth * 13}px; item.type === "component" && "hidden"
`
)} )}
></div> ></div>
{item.type === "folder" && ( {item.type === "folder" && (
@ -58,7 +63,12 @@ export const edPageTreeRender: NodeRender<CompItem> = (
{!isOpen && <FolderClose />} {!isOpen && <FolderClose />}
</> </>
)} )}
<div className="flex flex-1 pl-1"> <div
className={cx(
"flex flex-1 px-1",
item.type === "component" && "border-r"
)}
>
{local.renaming ? ( {local.renaming ? (
<input <input
value={local.rename_to} value={local.rename_to}
@ -100,105 +110,28 @@ export const edPageTreeRender: NodeRender<CompItem> = (
)} )}
</div> </div>
{!local.renaming && ( {item.type === "component" && (
<div className="flex pr-2 items-stretch h-[18px] space-x-[2px] text-[10px]">
{item.type === "folder" && (
<>
<div <div
className="btn transition-all bg-white flex items-center border px-1 hover:border-blue-300 hover:bg-blue-100" className="transition-all bg-white flex items-center px-1 hover:border-blue-300 hover:bg-blue-100 opacity-20 hover:opacity-100"
onClick={(e) => {
e.stopPropagation();
compPicker.tree.push({
id: "",
parent: item.id,
text: "",
data: {
id: "",
name: "",
type: "folder",
},
});
p.render();
}}
>
+ Folder
</div>
<div
className="btn transition-all bg-white flex items-center border px-1 hover:border-blue-300 hover:bg-blue-100"
onClick={(e) => {
e.stopPropagation();
p.ui.popup.page.form = {
id_site: p.site.id,
id_folder: item.id === "root" ? null : item.id,
};
p.render();
}}
>
+ Page
</div>
</>
)}
{item.id !== "root" && (
<>
<div
className="btn transition-all bg-white flex items-center border px-1 hover:border-blue-300 hover:bg-blue-100"
onClick={(e) => {
e.stopPropagation();
if (item.type === "folder") {
local.rename_to = item.name;
local.renaming = true;
local.render();
} else {
p.ui.popup.page.form = item;
p.render();
}
}}
>
<EditIcon />
</div>
<div
className="btn transition-all bg-white flex items-center border px-1 hover:border-red-300 hover:bg-red-100"
onClick={async (e) => { onClick={async (e) => {
e.stopPropagation(); e.stopPropagation();
if (confirm("Deletting cannot be undone. Are you sure ?")) {
if (item.type === "folder") {
await db.page.updateMany({
where: { id_folder: node.id as string },
data: {
id_folder:
node.parent === "root"
? null
: (node.parent as string),
},
});
await db.page_folder.update({
where: { id: node.id as string },
data: {
is_deleted: true,
},
});
} else {
await db.page.update({
where: { id: node.id as string },
data: {
is_deleted: true,
},
});
}
await reloadCompPicker(p); if (p.ui.popup.comp.open) {
p.ui.popup.comp.open(item.id);
} }
p.ui.popup.comp.open = null;
console.log(active.item_id, compPicker.active_id);
active.item_id = compPicker.active_id;
compPicker.active_id = "";
p.page.render();
}} }}
> >
<DeleteIcon /> <ImageIcon />
</div>
</>
)}
</div> </div>
)} )}
</div> </div>
</div>
); );
}; };
@ -206,7 +139,7 @@ const Name: FC<{ name: string }> = ({ name }) => {
if (name.startsWith("layout::")) { if (name.startsWith("layout::")) {
return ( return (
<div className="flex items-center"> <div className="flex items-center">
<div className="border border-green-600 text-green-600 mr-1 font-mono text-[8px] px-1 bg-white "> <div className="border border-green-600 text-green-600 mr-1 font-mono text-[10px] px-1 bg-white ">
LAYOUT LAYOUT
</div> </div>
<div>{name.substring("layout::".length)}</div> <div>{name.substring("layout::".length)}</div>
@ -216,7 +149,13 @@ const Name: FC<{ name: string }> = ({ name }) => {
return <div>{name}</div>; return <div>{name}</div>;
}; };
const ImageIcon = () => (
<div
dangerouslySetInnerHTML={{
__html: `<svg width="12" height="12" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.24182 2.32181C3.3919 2.23132 3.5784 2.22601 3.73338 2.30781L12.7334 7.05781C12.8974 7.14436 13 7.31457 13 7.5C13 7.68543 12.8974 7.85564 12.7334 7.94219L3.73338 12.6922C3.5784 12.774 3.3919 12.7687 3.24182 12.6782C3.09175 12.5877 3 12.4252 3 12.25V2.75C3 2.57476 3.09175 2.4123 3.24182 2.32181ZM4 3.57925V11.4207L11.4288 7.5L4 3.57925Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
}}
></div>
);
const DeleteIcon = () => ( const DeleteIcon = () => (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -70,7 +70,7 @@ export const EdPopPage = () => {
}} }}
className={cx("absolute inset-[5%] bg-white flex")} className={cx("absolute inset-[5%] bg-white flex")}
> >
<div className="relative flex flex-1 items-stretch text-[12px] overflow-auto"> <div className="relative flex flex-1 items-stretch text-[14px] overflow-auto">
{pagePicker.status === "loading" && ( {pagePicker.status === "loading" && (
<Loading note="listing-page" backdrop={false} /> <Loading note="listing-page" backdrop={false} />
)} )}