wip fix tree-rebuild

This commit is contained in:
Rizky 2023-12-15 03:41:29 +07:00
parent 8188589065
commit 3c25a62192
16 changed files with 388 additions and 226 deletions

View File

@ -12,7 +12,6 @@ export const comp_load: SAction["comp"]["load"] = async function (
) { ) {
const result: Record<string, IScopeComp> = {}; const result: Record<string, IScopeComp> = {};
for (const id of ids) { for (const id of ids) {
const root = await loadComponent(id, this);
} }
return result; return result;
}; };

View File

@ -6,7 +6,7 @@ import { GenMetaP } from "../../../../web/src/nova/vi/utils/types";
import { IItem } from "../../../../web/src/utils/types/item"; import { IItem } from "../../../../web/src/utils/types/item";
import { DPage } from "../../../../web/src/utils/types/root"; import { DPage } from "../../../../web/src/utils/types/root";
import { SAction } from "../actions"; import { SAction } from "../actions";
import { loadComponent } from "../editor/load-component"; import { loadComponent, userSyncComponent } from "../editor/load-component";
import { parseJs } from "../editor/parser/parse-js"; import { parseJs } from "../editor/parser/parse-js";
import { activity } from "../entity/activity"; import { activity } from "../entity/activity";
import { conns } from "../entity/conn"; import { conns } from "../entity/conn";
@ -201,6 +201,8 @@ const scanMeta = async (doc: DPage, sync: SyncConnection) => {
}); });
} }
await loading[id]; await loading[id];
} else {
userSyncComponent(sync, id);
} }
if (docs.comp[id]) { if (docs.comp[id]) {
@ -239,6 +241,6 @@ const scanMeta = async (doc: DPage, sync: SyncConnection) => {
comps[id] = { id, snapshot: await gzipAsync(snap.bin) }; comps[id] = { id, snapshot: await gzipAsync(snap.bin) };
} }
} }
return { meta: simplifyMeta(meta), comps, entry }; return { meta: simplifyMeta(meta), comps, entry };
}; };

View File

@ -6,10 +6,11 @@ import { gzipAsync } from "../entity/zlib";
import { sendWS } from "../sync-handler"; import { sendWS } from "../sync-handler";
import { SyncConnection, SyncType } from "../type"; import { SyncConnection, SyncType } from "../type";
export const loadComponent = async (id: string, sync: SyncConnection) => { export const loadComponent = async (comp_id: string, sync: SyncConnection) => {
let snap = snapshot.get("comp", id); let snap = snapshot.get("comp", comp_id);
let ydoc = docs.comp[id]; let ydoc = docs.comp[comp_id];
const conf = sync.conf; const conf = sync.conf;
if (!conf) return undefined; if (!conf) return undefined;
const createUndoManager = async (root: Y.Map<any>) => { const createUndoManager = async (root: Y.Map<any>) => {
@ -21,25 +22,28 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
}; };
const attachOnUpdate = async (doc: Y.Doc, um: Y.UndoManager) => { const attachOnUpdate = async (doc: Y.Doc, um: Y.UndoManager) => {
snapshot.set("comp", id, "id_doc", um.doc.clientID); snapshot.set("comp", comp_id, "id_doc", um.doc.clientID);
doc.on("update", async (update: Uint8Array, origin: any) => { doc.on("update", async (update: Uint8Array, origin: any) => {
const bin = Y.encodeStateAsUpdate(doc); const bin = Y.encodeStateAsUpdate(doc);
snapshot.set("comp", id, "bin", bin); snapshot.set("comp", comp_id, "bin", bin);
const sv_local = await gzipAsync(update); const sv_local = await gzipAsync(update);
const all = user.active.findAll({ comp_id: id }); const all = user.active.findAll({ comp_id: comp_id });
all.map((e) => { all.map((e) => {
if (origin !== um) { if (origin !== um) {
if (e.client_id === origin) return; if (e.client_id === origin) return;
} }
const ws = conns.get(e.client_id)?.ws; const ws = conns.get(e.client_id)?.ws;
if (ws) { if (ws) {
sendWS(ws, { sendWS(ws, {
type: SyncType.Event, type: SyncType.Event,
event: "remote_svlocal", event: "remote_svlocal",
data: { type: "comp", sv_local, id }, data: { type: "comp", sv_local, id: comp_id },
}); });
} }
}); });
@ -51,16 +55,16 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
}; };
if (!snap && !ydoc) { if (!snap && !ydoc) {
const comp = await db.component.findFirst({ where: { id } }); const comp = await db.component.findFirst({ where: { id: comp_id } });
if (comp) { if (comp) {
const doc = new Y.Doc(); const doc = new Y.Doc();
let root = doc.getMap("map"); let root = doc.getMap("map");
syncronize(root, { id, root: comp.content_tree }); syncronize(root, { id: comp_id, root: comp.content_tree });
const um = await createUndoManager(root); const um = await createUndoManager(root);
docs.comp[id] = { docs.comp[comp_id] = {
doc: doc as any, doc: doc as any,
id, id: comp_id,
um, um,
}; };
@ -69,7 +73,7 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
snapshot.update({ snapshot.update({
bin, bin,
id, id: comp_id,
type: "comp", type: "comp",
name: comp.name, name: comp.name,
ts: Date.now(), ts: Date.now(),
@ -86,23 +90,23 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
}); });
return { return {
id: id, id: comp_id,
name: comp.name, name: comp.name,
snapshot: await gzipAsync(bin), snapshot: await gzipAsync(bin),
}; };
} }
} else if (snap && !ydoc) { } else if (snap && !ydoc) {
const doc = new Y.Doc(); const doc = new Y.Doc();
snapshot.set("comp", id, "id_doc", doc.clientID); snapshot.set("comp", comp_id, "id_doc", doc.clientID);
Y.applyUpdate(doc, snap.bin); Y.applyUpdate(doc, snap.bin);
let root = doc.getMap("map"); let root = doc.getMap("map");
const um = await createUndoManager(root); const um = await createUndoManager(root);
await attachOnUpdate(doc, um); await attachOnUpdate(doc, um);
docs.comp[id] = { docs.comp[comp_id] = {
doc: doc as any, doc: doc as any,
id, id: comp_id,
um, um,
}; };
@ -112,11 +116,11 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
user_id: sync.user_id, user_id: sync.user_id,
site_id: conf.site_id, site_id: conf.site_id,
page_id: conf.page_id, page_id: conf.page_id,
comp_id: id, comp_id: comp_id,
}); });
return { return {
id: id, id: comp_id,
name: snap.name, name: snap.name,
snapshot: await gzipAsync(snap.bin), snapshot: await gzipAsync(snap.bin),
}; };
@ -127,7 +131,7 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
user_id: sync.user_id, user_id: sync.user_id,
site_id: conf.site_id, site_id: conf.site_id,
page_id: conf.page_id, page_id: conf.page_id,
comp_id: id, comp_id: comp_id,
}); });
return { return {
@ -137,3 +141,15 @@ export const loadComponent = async (id: string, sync: SyncConnection) => {
}; };
} }
}; };
export const userSyncComponent = (sync: SyncConnection, comp_id: string) => {
const conf = sync.conf;
user.active.add({
client_id: sync.client_id,
user_id: sync.user_id,
site_id: conf?.site_id || "",
page_id: conf?.page_id || "",
comp_id: comp_id,
});
};

View File

@ -17,7 +17,6 @@ export const user = {
page_id: string; page_id: string;
comp_id?: string; comp_id?: string;
client_id: string; client_id: string;
select: "" | "comp" | "item" | "section" | "text";
}, },
"client_id" "client_id"
>("client_id"), >("client_id"),

View File

@ -33,6 +33,7 @@ export const syncHandler: WebSocketHandler<WSData> = {
close(ws) { close(ws) {
const client_id = wconns.get(ws); const client_id = wconns.get(ws);
if (client_id) { if (client_id) {
user.active.delAll({ client_id });
activity.site.disconnect(ws); activity.site.disconnect(ws);
conns.delete(client_id); conns.delete(client_id);
wconns.delete(ws); wconns.delete(ws);

View File

@ -1,9 +1,9 @@
import { NodeModel } from "@minoru/react-dnd-treeview";
import { compress, decompress } from "wasm-gzip"; import { compress, decompress } from "wasm-gzip";
import { IItem } from "../../../../utils/types/item"; import { IItem } from "../../../../utils/types/item";
import { DComp } from "../../../../utils/types/root"; import { DComp } from "../../../../utils/types/root";
import { genMeta } from "../../../vi/meta/meta"; import { genMeta } from "../../../vi/meta/meta";
import { IMeta, PG } from "../ed-global"; import { IMeta, PG } from "../ed-global";
import { NodeModel } from "@minoru/react-dnd-treeview";
import { pushTreeNode } from "../tree/build/push-tree"; import { pushTreeNode } from "../tree/build/push-tree";
export const loadcomp = { export const loadcomp = {
@ -39,87 +39,108 @@ export const loadComponent = async (p: PG, id_comp: string) => {
export const loadCompSnapshot = async ( export const loadCompSnapshot = async (
p: PG, p: PG,
id_comp: string, comp_id: string,
snapshot: Uint8Array snapshot: Uint8Array
) => { ) => {
const doc = new Y.Doc() as DComp; const doc = new Y.Doc() as DComp;
Y.applyUpdate(doc as any, decompress(snapshot)); Y.applyUpdate(doc as any, decompress(snapshot));
const mitem = doc.getMap("map").get("root"); const mitem = doc.getMap("map").get("root");
if (mitem) { if (mitem) {
if (typeof p.comp.list[id_comp]?.on_update === "function") { if (typeof p.comp.list[comp_id]?.on_update === "function") {
doc.off("update", p.comp.list[id_comp].on_update); doc.off("update", p.comp.list[comp_id].on_update);
} }
const meta: Record<string, IMeta> = {}; const updated = updateComponentMeta(p, doc, comp_id);
const tree: NodeModel<IMeta>[] = []; if (updated) {
const item = mitem.toJSON() as IItem; const { meta, tree } = updated;
p.comp.loaded[id_comp] = { if (p.comp.list[comp_id]) {
comp: item, p.comp.list[comp_id].meta = meta;
}; p.comp.list[comp_id].tree = tree;
genMeta( } else {
{ p.comp.list[comp_id] = {
comps: p.comp.loaded, comp: { id: comp_id, snapshot },
meta, doc,
on: { meta,
visit(m) { tree,
pushTreeNode(p, m, meta, tree); async on_update(bin, origin) {
if (origin === "sv_remote" || origin === "local") {
return;
}
if (m.parent) { const res = await p.sync.yjs.sv_local(
if (m.parent.id === "root") { "comp",
if (m.item.id === item.id) { comp_id,
m.mitem = mitem; Buffer.from(compress(bin))
} );
} else {
const parent = meta[m.parent.id];
if (parent.mitem) { if (res) {
parent.mitem.get("childs")?.forEach((child) => { const diff_local = Y.encodeStateAsUpdate(
if (child.get("id") === m.item.id) { doc as any,
m.mitem = child; decompress(res.sv)
} );
}); Y.applyUpdate(doc as any, decompress(res.diff), "local");
} await p.sync.yjs.diff_local(
"comp",
comp_id,
Buffer.from(compress(diff_local))
);
const updated = updateComponentMeta(p, doc, comp_id);
if (updated) {
p.comp.list[comp_id].meta = updated.meta;
p.comp.list[comp_id].tree = updated.tree;
} }
p.render();
} }
}, },
}, };
note: "load-comp",
},
{ item, ignore_first_component: true }
);
p.comp.list[id_comp] = { doc.on("update", p.comp.list[comp_id].on_update);
comp: { id: id_comp, snapshot }, }
doc, }
meta,
tree,
async on_update(bin, origin) {
if (origin === "sv_remote" || origin === "local") {
return;
}
const res = await p.sync.yjs.sv_local(
"comp",
id_comp,
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 p.sync.yjs.diff_local(
"comp",
id_comp,
Buffer.from(compress(diff_local))
);
p.render();
}
},
};
doc.on("update", p.comp.list[id_comp].on_update);
} }
}; };
export const updateComponentMeta = (p: PG, doc: DComp, comp_id: string) => {
const mitem = doc.getMap("map").get("root");
if (!mitem) return;
const meta: Record<string, IMeta> = {};
const tree: NodeModel<IMeta>[] = [];
const item = mitem.toJSON() as IItem;
p.comp.loaded[comp_id] = {
comp: item,
};
genMeta(
{
comps: p.comp.loaded,
meta,
on: {
visit(m) {
pushTreeNode(p, m, meta, tree);
if (m.parent) {
if (m.parent.id === "root") {
if (m.item.id === item.id) {
m.mitem = mitem;
}
} else {
const parent = meta[m.parent.id];
if (parent.mitem) {
parent.mitem.get("childs")?.forEach((child) => {
if (child.get("id") === m.item.id) {
m.mitem = child;
}
});
}
}
}
},
},
note: "load-comp",
},
{ item, ignore_first_component: true }
);
return { meta, tree };
};

View File

@ -8,6 +8,7 @@ import { EmptySite, PG } from "./ed-global";
import { treeRebuild } from "./tree/build"; import { treeRebuild } from "./tree/build";
import { reloadPage } from "./ed-route"; import { reloadPage } from "./ed-route";
import { loadSite } from "./ed-site"; import { loadSite } from "./ed-site";
import { updateComponentMeta } from "./comp/load";
const decoder = new TextDecoder(); const decoder = new TextDecoder();
@ -172,9 +173,20 @@ export const edInitSync = (p: PG) => {
sv, sv,
diff diff
); );
if (res) { if (res) {
Y.applyUpdate(doc, decompress(res.diff), "sv_remote"); Y.applyUpdate(doc, decompress(res.diff), "sv_remote");
await treeRebuild(p, { note: "sv_remote" }); if (data.type === "page") {
await treeRebuild(p, { note: "sv_remote" });
} else {
const updated = updateComponentMeta(p, doc, data.id);
if (updated) {
p.comp.list[data.id].meta = updated.meta;
p.comp.list[data.id].tree = updated.tree;
}
await treeRebuild(p, { note: "sv_remote" });
}
p.render();
} }
} }
}, },

View File

@ -0,0 +1,66 @@
import { MItem } from "../../../../../utils/types/item";
import { FNAdv } from "../../../../../utils/types/meta-fn";
import { PG } from "../../../logic/ed-global";
export const edMonacoDefaultVal = (p: PG, adv: FNAdv, mitem: MItem) => {
let val = "";
if (p.ui.popup.script.type === "item") {
const mode = p.ui.popup.script.mode;
val = (typeof adv[mode] === "string" ? adv[mode] : "") as any;
if (val === "") {
if (mode === "js") {
val = `<div {...props}>{children}</div>`;
} else if (mode === "css") {
val = `\
& {
display: flex;
// &.mobile {}
// &.desktop {}
// &:hover {}
}`;
}
}
} else if (
p.ui.popup.script.type === "prop-master" ||
p.ui.popup.script.type === "prop-instance"
) {
const mprops = mitem?.get("component")?.get("props");
if (mprops) {
const mprop = mprops.get(p.ui.popup.script.prop_name);
if (mprop) {
const kind = p.ui.popup.script.prop_kind;
if (kind === "value") {
val = mprop.get("value");
} else if (kind === "gen") {
val =
mprop.get("gen") ||
`\
async () => {
return \`""\`;
}`;
} else if (kind === "visible") {
val = mprop.get("visible") || "true";
} else if (kind === "option") {
val =
mprop.get("meta")?.get("options") ||
`\
[
{
label: "yes",
value: "y"
},
{
label: "no",
value: "n"
},
]`;
}
}
}
}
return val;
};

View File

@ -1,7 +1,7 @@
import type { Monaco, OnMount } from "@monaco-editor/react"; import type { Monaco, OnMount } from "@monaco-editor/react";
import { createStore } from "idb-keyval"; import { createStore } from "idb-keyval";
import trim from "lodash.trim"; import trim from "lodash.trim";
import { useEffect } from "react"; import { FC, useEffect } from "react";
import { compress } from "wasm-gzip"; import { compress } from "wasm-gzip";
import { useGlobal, useLocal } from "web-utils"; import { useGlobal, useLocal } from "web-utils";
import { jscript } from "../../../../../utils/script/jscript"; import { jscript } from "../../../../../utils/script/jscript";
@ -10,6 +10,7 @@ import { monacoTypings } from "../../../../../utils/script/typings";
import { EDGlobal, IMeta, active } from "../../../logic/ed-global"; import { EDGlobal, IMeta, active } from "../../../logic/ed-global";
import { getMetaById } from "../../../logic/tree/build"; import { getMetaById } from "../../../logic/tree/build";
import { declareScope } from "./scope"; import { declareScope } from "./scope";
import { edMonacoDefaultVal } from "./default-val";
const scriptEdit = { const scriptEdit = {
timeout: null as any, timeout: null as any,
@ -17,7 +18,7 @@ const scriptEdit = {
const encode = new TextEncoder(); const encode = new TextEncoder();
export type MonacoEditor = Parameters<OnMount>[0]; export type MonacoEditor = Parameters<OnMount>[0];
export const ScriptMonaco = () => { export const EdScriptMonaco: FC<{}> = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({ const local = useLocal({
editor: null as null | MonacoEditor, editor: null as null | MonacoEditor,
@ -26,6 +27,7 @@ export const ScriptMonaco = () => {
init: false, init: false,
value: "", value: "",
historyOpen: false, historyOpen: false,
mode: "",
idbstore: createStore(`prasi-page-${p.page.cur.id}`, "script-history"), idbstore: createStore(`prasi-page-${p.page.cur.id}`, "script-history"),
}); });
@ -53,6 +55,11 @@ export const ScriptMonaco = () => {
const monaco = local.monaco; const monaco = local.monaco;
if (monaco && editor) { if (monaco && editor) {
if (local.mode !== p.ui.popup.script.mode) {
local.init = false;
local.mode = p.ui.popup.script.mode;
}
if (!local.init) { if (!local.init) {
if (p.ui.popup.script.mode === "js") { if (p.ui.popup.script.mode === "js") {
monaco.editor.getModels().forEach((model) => { monaco.editor.getModels().forEach((model) => {
@ -101,7 +108,7 @@ export const ScriptMonaco = () => {
} }
} }
})(); })();
}, [active.item_id, local.monaco, local.editor]); }, [active.item_id, local.monaco, local.editor, p.ui.popup.script.mode]);
if (!meta) return null; if (!meta) return null;
@ -110,41 +117,45 @@ export const ScriptMonaco = () => {
item.adv = adv; item.adv = adv;
const doEdit = async (newval: string, all?: boolean) => { const doEdit = async (newval: string, all?: boolean) => {
if (local.editor && jscript.prettier.standalone) { if (local.editor) {
const text = trim( const prettier = jscript.prettier.standalone;
await jscript.prettier.standalone.format( const prettier_ts = jscript.prettier.ts;
all const prettier_estree = jscript.prettier.estree;
? newval
: local.editor?.getValue().replace(/\{\s*children\s*\}/gi, newval)
),
"; \n"
);
local.editor.executeEdits(null, [ if (prettier && prettier_estree && prettier_ts) {
{ const text = trim(
range: { await prettier.format(
startLineNumber: 0, all
startColumn: 0, ? newval
endColumn: Number.MAX_SAFE_INTEGER, : local.editor
endLineNumber: Number.MAX_SAFE_INTEGER, ?.getValue()
.replace(/\{\s*children\s*\}/gi, newval),
{
parser: "typescript",
plugins: [prettier_ts, prettier_estree],
}
),
"; \n"
);
local.editor.executeEdits(null, [
{
range: {
startLineNumber: 0,
startColumn: 0,
endColumn: Number.MAX_SAFE_INTEGER,
endLineNumber: Number.MAX_SAFE_INTEGER,
},
text,
}, },
text, ]);
}, }
]);
} }
}; };
p.script.do_edit = doEdit; p.script.do_edit = doEdit;
let mitem = meta.mitem; let mitem = meta.mitem;
if (p.ui.popup.script.type === "item") {
val = (
typeof adv[p.ui.popup.script.mode] === "string"
? adv[p.ui.popup.script.mode]
: ""
) as any;
}
if (!mitem) { if (!mitem) {
active.item_id = ""; active.item_id = "";
return <div>no mitem</div>; return <div>no mitem</div>;
@ -155,44 +166,7 @@ export const ScriptMonaco = () => {
} }
} }
if ( val = edMonacoDefaultVal(p, adv, mitem);
p.ui.popup.script.type === "prop-master" ||
p.ui.popup.script.type === "prop-instance"
) {
const mprops = mitem?.get("component")?.get("props");
if (mprops) {
const mprop = mprops.get(p.ui.popup.script.prop_name);
if (mprop) {
const kind = p.ui.popup.script.prop_kind;
if (kind === "value") {
val = mprop.get("value");
} else if (kind === "gen") {
val =
mprop.get("gen") ||
`\
async () => {
return \`""\`;
}`;
} else if (kind === "visible") {
val = mprop.get("visible") || "true";
} else if (kind === "option") {
val =
mprop.get("meta")?.get("options") ||
`\
[
{
label: "yes",
value: "y"
},
{
label: "no",
value: "n"
},
]`;
}
}
}
}
return ( return (
<Editor <Editor

View File

@ -4,7 +4,7 @@ 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 } from "../../../logic/ed-global";
import { propPopover } from "../../side/prop-master/prop-form"; import { propPopover } from "../../side/prop-master/prop-form";
import { ScriptWorkbench } from "./workbench"; import { EdScriptWorkbench } from "./workbench";
export const EdPopScript = () => { export const EdPopScript = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
@ -80,7 +80,7 @@ export const EdPopScript = () => {
)} )}
> >
{!jscript.editor && <Loading note={"js-editor"} backdrop={false} />} {!jscript.editor && <Loading note={"js-editor"} backdrop={false} />}
{jscript.editor && <ScriptWorkbench />} {jscript.editor && <EdScriptWorkbench />}
</div> </div>
</div> </div>
</Modal> </Modal>

View File

@ -0,0 +1,46 @@
import { FC } from "react";
import { Button } from "../../../../../utils/ui/form/Button";
import { useGlobal } from "web-utils";
import { EDGlobal } from "../../../logic/ed-global";
export const EdScriptSnippet: FC<{}> = ({}) => {
const p = useGlobal(EDGlobal, "EDITOR");
return (
<div className="flex items-center space-x-1 pl-2 border-l">
<Button
className={cx(css`
width: auto !important;
padding-left: 5px;
padding-right: 5px;
font-size: 12px;
`)}
onClick={() => {
console.log(p.script.do_edit);
p.script.do_edit(
`\
<div {...props}>
<Local
name="local"
value={
{
//local object
}
}
effect={async (local) => {
//local effect
}}
>
{children}
</Local>
</div>
`,
true
);
}}
>
&lt;Local/&gt;
</Button>
</div>
);
};

View File

@ -1,53 +1,57 @@
import { useGlobal } from "web-utils"; import { useGlobal } from "web-utils";
import { ScriptMonaco } from "./monaco"; import { EdScriptMonaco } from "./monaco";
import { EDGlobal, active } from "../../../logic/ed-global"; import { EDGlobal, active } from "../../../logic/ed-global";
import { IItem } from "../../../../../utils/types/item"; import { IItem } from "../../../../../utils/types/item";
import { EdScriptSnippet } from "./snippet";
export const ScriptWorkbench = () => { export const EdScriptWorkbench = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
return ( return (
<div className="flex flex-1 items-stretch"> <div className="flex flex-1 items-stretch">
<div className="flex flex-1 flex-col "> <div className="flex flex-1 flex-col ">
<div className="flex p-2 border-b space-x-2"> <div className="flex border-b">
{p.ui.popup.script.type === "prop-master" ? ( {p.ui.popup.script.type === "prop-master" ? (
<CompTitle /> <CompTitle />
) : ( ) : (
<> <>
{[ <div className="flex p-2 space-x-1">
{ type: "js", color: "#e9522c" }, {[
{ type: "css", color: "#188228" }, { type: "js", color: "#e9522c" },
{ type: "html", color: "#2c3e83" }, { type: "css", color: "#188228" },
].map((e) => { { type: "html", color: "#2c3e83" },
return ( ].map((e) => {
<div return (
key={e.type} <div
className={cx( key={e.type}
css` className={cx(
color: ${e.color}; css`
border: 1px solid ${e.color}; color: ${e.color};
`, border: 1px solid ${e.color};
"uppercase text-white text-[12px] cursor-pointer transition-all hover:opacity-100 w-[40px] text-center", `,
p.ui.popup.script.mode === e.type "uppercase text-white text-[12px] cursor-pointer flex items-center justify-center transition-all hover:opacity-100 w-[40px] text-center",
? css` p.ui.popup.script.mode === e.type
background: ${e.color}; ? css`
color: white; background: ${e.color};
` color: white;
: "opacity-30" `
)} : "opacity-30"
onClick={() => { )}
p.ui.popup.script.mode = e.type as any; onClick={() => {
p.render(); p.ui.popup.script.mode = e.type as any;
}} p.render();
> }}
{e.type} >
</div> {e.type}
); </div>
})} );
})}
</div>
{p.ui.popup.script.mode === "js" && <EdScriptSnippet />}
</> </>
)} )}
</div> </div>
<div className="relative flex flex-1"> <div className="relative flex flex-1">
<ScriptMonaco /> <EdScriptMonaco />
</div> </div>
</div> </div>
</div> </div>

View File

@ -26,7 +26,15 @@ export const EdSidePropComp: FC<{ meta: IMeta }> = ({ meta }) => {
const TypedTree = DNDTree<PropItem>; const TypedTree = DNDTree<PropItem>;
let filtered = [] as NodeModel<PropItem>[]; let filtered = [] as NodeModel<PropItem>[];
const mprops = meta.mitem?.get("component")?.get("props"); let mprops = meta.mitem?.get("component")?.get("props");
if (!mprops) {
const mcomp = meta.mitem?.get("component");
if (mcomp) {
mcomp.set("props", new Y.Map() as any);
mprops = mcomp.get("props");
}
}
if (mprops && meta.mitem) { if (mprops && meta.mitem) {
mprops.forEach((m, key) => { mprops.forEach((m, key) => {
filtered.push({ filtered.push({
@ -139,13 +147,13 @@ export const EdSidePropComp: FC<{ meta: IMeta }> = ({ meta }) => {
/> />
</DndProvider> </DndProvider>
<div <div
className="m-1 border border-blue-200 px-2 self-start text-[13px] hover:bg-blue-100 cursor-pointer" className="m-1 border border-blue-200 px-2 self-start text-[13px] hover:bg-blue-100 cursor-pointer select-none"
onClick={() => { onClick={() => {
if (mprops) { if (mprops) {
const indexes: (number | undefined)[] = []; const indexes: (number | undefined)[] = [];
mprops.forEach((e) => indexes.push(e.get("idx"))); mprops.forEach((e) => indexes.push(e.get("idx")));
let idx: any = (indexes.sort().pop() || 0) + 1; let idx: any = (indexes.sort().pop() || 0) + 1;
const name = `prop_${idx + 1}`; const name = `prop_${indexes.length === 0 ? 1 : idx + 1}`;
const map = new Y.Map() as FMCompDef; const map = new Y.Map() as FMCompDef;
syncronize(map, { syncronize(map, {
idx: idx, idx: idx,

View File

@ -13,10 +13,11 @@ export const propPopover = {
name: "", name: "",
}; };
export const EdPropPopoverForm: FC<{ mprop: FMCompDef; name: string }> = ({ export const EdPropPopoverForm: FC<{
mprop, mprop: FMCompDef;
name, name: string;
}) => { closing: boolean;
}> = ({ mprop, name, closing }) => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
const mmeta = mprop.get("meta"); const mmeta = mprop.get("meta");
const local = useLocal({ const local = useLocal({
@ -28,7 +29,8 @@ export const EdPropPopoverForm: FC<{ mprop: FMCompDef; name: string }> = ({
return ( return (
<div <div
className={cx( className={cx(
"flex text-sm flex-col items-stretch space-y-1 py-1 w-[300px]" "flex text-sm flex-col items-stretch space-y-1 py-1 w-[300px]",
closing && "hidden"
)} )}
> >
<div className="px-2 py-1 flex space-x-1"> <div className="px-2 py-1 flex space-x-1">

View File

@ -5,6 +5,7 @@ import { FMCompDef, FNCompDef } from "../../../../../utils/types/meta-fn";
import { Popover } from "../../../../../utils/ui/popover"; import { Popover } from "../../../../../utils/ui/popover";
import { EdPropPopoverForm, propPopover } from "./prop-form"; import { EdPropPopoverForm, propPopover } from "./prop-form";
import { TypedMap } from "yjs-types"; import { TypedMap } from "yjs-types";
import { useLocal } from "web-utils";
export type PropItem = { export type PropItem = {
name: string; name: string;
@ -18,6 +19,7 @@ export const EdPropCompTreeItem: FC<{
params: Parameters<NodeRender<PropItem>>[1]; params: Parameters<NodeRender<PropItem>>[1];
render: () => void; render: () => void;
}> = ({ node, params, render }) => { }> = ({ node, params, render }) => {
const local = useLocal({ closing: false });
if (node.id === "root") { if (node.id === "root") {
return <></>; return <></>;
} }
@ -51,14 +53,24 @@ export const EdPropCompTreeItem: FC<{
popoverClassName="bg-white shadow-lg border border-slate-300" popoverClassName="bg-white shadow-lg border border-slate-300"
onOpenChange={(open) => { onOpenChange={(open) => {
if (!open) { if (!open) {
propPopover.name = ""; local.closing = true;
local.render();
setTimeout(() => {
propPopover.name = "";
render();
});
} else { } else {
local.closing = false;
propPopover.name = node.text; propPopover.name = node.text;
render();
} }
render();
}} }}
content={ content={
<EdPropPopoverForm mprop={node.data.mprop} name={node.text} /> <EdPropPopoverForm
closing={local.closing}
mprop={node.data.mprop}
name={node.text}
/>
} }
className="flex-1 pl-1 hover:bg-blue-100 cursor-pointer items-center flex" className="flex-1 pl-1 hover:bg-blue-100 cursor-pointer items-center flex"
> >

View File

@ -15,6 +15,24 @@ type CompilerOptions = Parameters<
export const jsMount = async (editor: MonacoEditor, monaco: Monaco, p?: PG) => { export const jsMount = async (editor: MonacoEditor, monaco: Monaco, p?: PG) => {
const m = monaco as any; const m = monaco as any;
if (editor.getModel()) {
const jsxHgController = new MonacoJsxSyntaxHighlight(getWorker(), monaco);
const { highlighter } = jsxHgController.highlighterBuilder({
editor: editor,
});
if (typeof editor.getModel === "function") {
highlighter();
}
editor.onDidChangeModelContent(() => {
if (typeof editor.getModel === "function") {
try {
highlighter();
} catch (e) {}
}
});
}
if (!m.customJSMounted) { if (!m.customJSMounted) {
m.customJSMounted = true; m.customJSMounted = true;
} else { } else {
@ -267,24 +285,6 @@ export const jsMount = async (editor: MonacoEditor, monaco: Monaco, p?: PG) => {
); );
setTimeout(() => { setTimeout(() => {
if (editor.getModel()) {
const jsxHgController = new MonacoJsxSyntaxHighlight(getWorker(), monaco);
const { highlighter } = jsxHgController.highlighterBuilder({
editor: editor,
});
if (typeof editor.getModel === "function") {
highlighter();
}
editor.onDidChangeModelContent(() => {
if (typeof editor.getModel === "function") {
try {
highlighter();
} catch (e) {}
}
});
}
editor.getAction("editor.action.formatDocument")?.run(); editor.getAction("editor.action.formatDocument")?.run();
}, 100); }, 100);
}; };