prasi-bun/app/web/src/render/editor/logic/tree-logic.tsx

505 lines
14 KiB
TypeScript

import { createId } from "@paralleldrive/cuid2";
import { syncronize } from "y-pojo";
import * as Y from "yjs";
import { IContent, MContent } from "../../../utils/types/general";
import { IItem, MItem } from "../../../utils/types/item";
import { FNAdv, FNCompDef } from "../../../utils/types/meta-fn";
import { IText } from "../../../utils/types/text";
import { DefaultScript } from "../panel/script/monaco/monaco-el";
import { fillID } from "../tools/fill-id";
import { newMap } from "../tools/yjs-tools";
import { instantiateComp, loadComponent } from "./comp";
import { ItemMeta, PG, WithRequired } from "./global";
export type REBUILD_MODE = "update" | "reset";
const DEBUG = false;
const DEBUG_MAX_ITEM: number = 3;
const DEBUG_MIN_ITEM: number = 0;
let DEBUG_CUR_IDX = 0;
export const rebuildTree = async (
p: PG,
opt?: { render?: () => void; mode?: REBUILD_MODE; note: string }
) => {
if (p.treePending) return;
p.treePending = new Promise<void>(async (resolve) => {
const _render = () => {
if (opt?.render) {
opt?.render();
} else {
p.render();
}
};
const mode = opt?.mode || "update";
if (mode === "reset") {
p.treeMeta = {};
p.compInstance = {};
}
p.treeFlatTemp = [];
if (p.mpage) {
if (DEBUG) {
DEBUG_CUR_IDX = 0;
console.clear();
}
const mpage = p.mpage.getMap("map").get("content_tree");
await mpage?.doc?.transact(async () => {
let parent_id = "root";
let includeTree = p.comp?.id ? false : true;
await Promise.all(
mpage?.get("childs")?.map(async (mitem) => {
await walk(p, mode, {
mitem,
parent_id,
depth: 0,
idx: 0,
includeTree,
});
}) || []
);
});
p.treeFlat = p.treeFlatTemp;
const root = p.treeFlat.find((e) => e.parent === "root");
if (
p.comp &&
root &&
p.comp.id === root.data.meta.comp?.id &&
p.comp.instance_id !== root.data.meta.item.id
) {
p.comp.instance_id = root.id;
}
}
resolve();
p.treePending = null;
_render();
});
};
export const walk = async (
p: PG,
mode: REBUILD_MODE,
val: {
mitem?: MContent;
item?: IContent;
minstance?: MItem;
parent_id: string;
parent_comp?: WithRequired<ItemMeta, "comp"> & { item: IItem };
parent_prop?: ItemMeta;
depth?: number;
idx?: number;
includeTree?: boolean;
instanceFound?: boolean;
skip?: boolean;
}
) => {
let item = val.item as IContent;
let mitem = val.mitem;
if (val.mitem) {
if (val.parent_comp) {
try {
const child_ids = val.parent_comp.comp.child_ids;
item = fillID(val.mitem.toJSON() as any, (e) => {
if (child_ids[e.id]) {
e.originalId = e.id;
e.id = child_ids[e.id];
}
return false;
}) as any;
} catch (e) {
return;
}
} else if (val.mitem && val.mitem.toJSON) {
item = val.mitem.toJSON() as any;
} else {
delete val.mitem;
}
}
if (val.parent_comp) {
const pchild_ids = val.parent_comp.comp?.child_ids;
if (pchild_ids && item.originalId) {
if (pchild_ids[item.originalId]) {
const walkInner = (item: IItem | IText) => {
if (item.originalId) {
const pid = pchild_ids[item.originalId];
if (pid) {
item.id = pid;
}
}
if (item.type === "item") {
for (const c of item.childs) {
walkInner(c);
}
}
};
walkInner(item as any);
}
}
}
if (item) {
let comp: ItemMeta["comp"] = undefined as any;
if (item.type === "item" && item.component?.id) {
const cid = item.component.id;
let doc = p.comps.doc[cid];
if (!doc) {
if (p.comp?.id === cid) {
await loadComponent(p, cid);
doc = p.comps.doc[cid];
} else {
loadComponent(p, cid).then(() => {
rebuildTree(p);
});
}
}
if (doc) {
const mcomp = doc?.getMap("map").get("content_tree");
if (mcomp) {
if (!p.compInstance[item.id]) {
p.compInstance[item.id] = {};
}
const child_ids = p.compInstance[item.id];
const itemnew = instantiateComp(p, item, mcomp, child_ids);
for (const [k, v] of Object.entries(itemnew)) {
if (k !== "id") (item as any)[k] = v;
}
comp = {
id: cid,
mcomp,
child_ids,
};
}
}
}
if (item.adv) {
let m = mitem;
let _item = item as IItem;
if (m && !p.script.active) {
const adv = m.get("adv");
if (adv && _item.adv) {
if (
typeof _item.adv.js === "string" &&
_item.adv.js.replace(/\W/gi, "") ===
DefaultScript.js.replace(/\W/gi, "")
) {
delete _item.adv.js;
delete _item.adv.jsBuilt;
adv?.delete("js");
adv?.delete("jsBuilt");
}
if (
typeof _item.adv.css === "string" &&
_item.adv.css.replace(/\W/gi, "") ===
DefaultScript.css.replace(/\W/gi, "")
) {
delete _item.adv.css;
adv?.delete("css");
}
}
}
}
const meta: ItemMeta = {
mitem: mitem,
item,
parent_id: p.cachedParentID[item.id] || val.parent_id,
parent_comp: val.parent_comp as any,
depth: val.depth || 0,
parent_prop: val.parent_prop,
indexedScope: p.treeMeta[item.id] ? p.treeMeta[item.id].indexedScope : {},
comp,
render: p.render,
};
if (DEBUG) {
DEBUG_CUR_IDX++;
if (DEBUG_CUR_IDX >= DEBUG_MIN_ITEM && DEBUG_CUR_IDX <= DEBUG_MAX_ITEM) {
const args = [
DEBUG_CUR_IDX.toString().padEnd(5, ".") +
(".".repeat(val.depth || 0) + item.name).padEnd(30, "_") +
(item.adv?.js ? "* " : "_ ") +
item.id,
].join(" ");
let color = "black";
if (comp) {
color = "red";
}
console.log(
"%c" + args,
`color:${color}`,
!!p.treeMeta[meta.item.id]?.comp,
`${val.parent_id}`
);
}
}
if (!val.skip && !item.hidden) {
if (p.treeMeta[meta.item.id]) {
for (const [k, v] of Object.entries(meta)) {
(p.treeMeta[meta.item.id] as any)[k] = v;
}
} else {
p.treeMeta[meta.item.id] = meta;
}
}
if (item.type === "item" && item.component?.id) {
let isRoot = false;
if (p.comp) {
if (p.comp.id === item.component.id) {
if (!val.instanceFound && p.treeFlatTemp.length === 0) {
p.treeFlatTemp.push({
parent: "root",
data: { meta, idx: 0 },
id: meta.item.id,
text: item.name,
});
isRoot = true;
val.includeTree = true;
}
if (p.comp.instance_id === item.id) {
p.treeFlatTemp.length = 0;
p.treeFlatTemp.push({
parent: "root",
data: { meta, idx: 0 },
id: meta.item.id,
text: item.name,
});
isRoot = true;
val.includeTree = true;
val.instanceFound = true;
}
}
}
if (val.includeTree && !isRoot) {
if (
p.treeFlatTemp.length > 0 ||
(p.treeFlatTemp.length === 0 && val.parent_id === "root")
) {
if (
!meta.parent_comp ||
(meta.parent_comp && meta.parent_comp.comp.id === p.comp?.id)
) {
val.includeTree = false;
p.treeFlatTemp.push({
parent: val.parent_id,
data: { meta, idx: val.idx || 0 },
id: item.id,
text: item.name,
});
}
}
}
const cid = item.component.id;
let doc = p.comps.doc[cid];
if (doc) {
const mcomp = doc.getMap("map").get("content_tree") as MItem;
let cprops: [string, FNCompDef][] = Object.entries(
item.component?.props || {}
).sort((a, b) => {
return a[1].idx - b[1].idx;
});
if (mcomp) {
const mprops = mcomp.get("component")?.get("props");
const iprops = mitem?.get("component")?.get("props");
if (mprops && iprops) {
for (const [name, cprop] of cprops) {
let mp = mprops.get(name);
if (mp) {
const mprop = mp?.toJSON() as FNCompDef;
const jsx_prop = iprops.get(name);
if (jsx_prop) {
if (mprop.meta?.type === "content-element") {
let mcontent = jsx_prop.get("content");
let isNew = false;
if (!mcontent) {
isNew = true;
jsx_prop.set(
"content",
newMap({
id: createId(),
name: name,
type: "item",
dim: { w: "full", h: "full" },
childs: [],
adv: {
css: "",
},
}) as any
);
mcontent = jsx_prop.get("content");
}
if (!!mcontent) {
const icontent = mcontent.toJSON() as IItem;
const adv = icontent.adv as FNAdv;
if (
Object.keys(icontent).length === 6 &&
adv &&
adv.js === undefined &&
adv.css === "" &&
adv.html === undefined &&
icontent.childs.length === 0
) {
isNew = true;
}
if (isNew) {
const defaultJSX = findDefaultJSX(p, mcontent);
if (defaultJSX && mcontent) {
syncronize(mcontent as any, defaultJSX);
}
mcontent = jsx_prop.get("content");
}
if (mcontent) {
await walk(p, mode, {
item: mcontent.toJSON() as any,
mitem: mcontent,
parent_id: item.id,
parent_comp: val.parent_comp,
parent_prop: meta,
idx: mprop.idx,
depth: (val.depth || 0) + 1,
includeTree: p.comp?.id !== item.component?.id,
instanceFound: val.instanceFound,
});
}
}
}
}
}
}
}
}
await Promise.all(
item.childs.map(async (child, idx) => {
if (meta.comp && meta.comp.mcomp) {
return await walk(p, mode, {
item: child,
parent_comp: meta as any,
mitem: meta.comp.mcomp.get("childs")?.get(idx),
parent_id: item.id,
parent_prop: val.parent_prop,
depth: (val.depth || 0) + 1,
includeTree: val.includeTree,
instanceFound: val.instanceFound,
});
}
})
);
}
} else if (item) {
if (
val.includeTree &&
(!meta.parent_comp ||
(meta.parent_comp && meta.parent_comp.comp.id === p.comp?.id))
) {
if (
p.treeFlatTemp.length > 0 ||
(p.treeFlatTemp.length === 0 &&
(val.parent_id === "root" || item.type === "section"))
) {
let parent = val.parent_id;
if (
p.treeFlatTemp.length === 0 &&
(val.parent_id === "root" || item.type === "section")
) {
parent = "root";
}
p.treeFlatTemp.push({
parent,
data: { meta, idx: val.idx || 0 },
id: meta.item.id,
text: item.name,
});
}
}
if (item.type !== "text" && Array.isArray(item.childs)) {
await Promise.all(
item.childs.map(async (child, idx) => {
const rmchild = mitem?.get("childs")?.get(idx);
const mchild = rmchild instanceof Y.Map ? rmchild : undefined;
return await walk(p, mode, {
idx,
item: child,
parent_comp: val.parent_comp,
mitem: mchild as MContent,
parent_id: item.id || "",
parent_prop: val.parent_prop,
depth: (val.depth || 0) + 1,
includeTree: val.includeTree,
instanceFound: val.instanceFound,
});
})
);
}
}
}
};
export const findDefaultJSX = (p: PG, mitem: MItem): IItem => {
let resetJSXProp: any = false;
if (mitem && mitem.parent && (mitem.parent as any).get("content")) {
let name = "";
(mitem as any).parent.parent.forEach((e: any, k: any) => {
if (e === mitem.parent) {
name = k;
}
});
if (name) {
try {
const cid = (mitem as any).parent.parent.parent.get("id");
const comp = p.comps.doc[cid];
if (comp) {
const mchilds = comp
.getMap("map")
.get("content_tree")
?.get("childs")
?.toJSON() as IItem[];
for (const c of mchilds) {
if (
c &&
c.name &&
c.name.startsWith("jsx:") &&
c.name.substring(4).trim() === name
) {
c.hidden = false;
c.name = name;
c.id = mitem.get("id") || "";
c.originalId = mitem.get("originalId") || "";
resetJSXProp = c;
}
}
}
} catch (e) {}
}
}
return resetJSXProp;
};