diff --git a/app/srv/ws/sync/editor/parser/parse-js.ts b/app/srv/ws/sync/editor/parser/parse-js.ts index ba16155f..0e46245c 100644 --- a/app/srv/ws/sync/editor/parser/parse-js.ts +++ b/app/srv/ws/sync/editor/parser/parse-js.ts @@ -10,7 +10,7 @@ export const parseJs = (meta: IMeta) => { const result = {} as { local?: typeof local | undefined; passprop?: typeof passprop | undefined; - props?: Record; + props?: Record; }; try { diff --git a/app/web/src/nova/ed/ed-mid.tsx b/app/web/src/nova/ed/ed-mid.tsx index 81444151..7f94351a 100644 --- a/app/web/src/nova/ed/ed-mid.tsx +++ b/app/web/src/nova/ed/ed-mid.tsx @@ -1,4 +1,5 @@ import { FC } from "react"; +import { active } from "./logic/ed-global"; import { EdAddItem } from "./panel/header/mid/add-item"; import { EdAddSection } from "./panel/header/mid/add-section"; import { EdAddText } from "./panel/header/mid/add-text"; @@ -23,7 +24,7 @@ export const EdMid: FC<{}> = () => {
ADD
- + {!active.comp_id && } diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index 281da1f5..3c214f1a 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -128,11 +128,12 @@ export const EDGlobal = { site: deepClone(EmptySite), site_dts: "", script: { - siteTypes: {} as Record, + site_types: {} as Record, loaded: false, do_edit: async (newval: string, all?: boolean) => {}, db: null as any, api: null as any, + init_local_effect: {} as Record, }, page: { root_id: "root", diff --git a/app/web/src/nova/ed/panel/header/mid/add-item.tsx b/app/web/src/nova/ed/panel/header/mid/add-item.tsx index 15c8eb3a..21428cea 100644 --- a/app/web/src/nova/ed/panel/header/mid/add-item.tsx +++ b/app/web/src/nova/ed/panel/header/mid/add-item.tsx @@ -1,6 +1,6 @@ import { createId } from "@paralleldrive/cuid2"; import { useGlobal, waitUntil } from "web-utils"; -import { MContent } from "../../../../../utils/types/general"; +import { IContent, MContent } from "../../../../../utils/types/general"; import { IItem } from "../../../../../utils/types/item"; import { EDGlobal, active } from "../../../logic/ed-global"; import { getMetaById } from "../../../logic/tree/build"; @@ -37,11 +37,12 @@ export const EdAddItem = () => { let mitem = meta.mitem; if (mitem) { + const item = meta.item as IContent; if ( - meta.item.type === "text" || - (meta.item.type === "item" && meta.item.component?.id) + item.type === "text" || + (item.type === "item" && item.component?.id) ) { - const parent_id = meta.parent_item.id; + const parent_id = meta.parent?.id || "root"; const parent = getMetaById( p, parent_id === "root" ? meta.item.id : parent_id diff --git a/app/web/src/nova/ed/panel/header/mid/add-section.tsx b/app/web/src/nova/ed/panel/header/mid/add-section.tsx index 6d229dcc..11b8dcc7 100644 --- a/app/web/src/nova/ed/panel/header/mid/add-section.tsx +++ b/app/web/src/nova/ed/panel/header/mid/add-section.tsx @@ -3,7 +3,6 @@ import { useGlobal } from "web-utils"; import { MContent } from "../../../../../utils/types/general"; import { ISection } from "../../../../../utils/types/section"; import { EDGlobal, active } from "../../../logic/ed-global"; -import { getMetaById } from "../../../logic/tree/build"; import { fillID } from "../../../logic/tree/fill-id"; import { TopBtn } from "../top-btn"; diff --git a/app/web/src/nova/ed/panel/header/mid/add-text.tsx b/app/web/src/nova/ed/panel/header/mid/add-text.tsx index 062230a0..cbfbaeeb 100644 --- a/app/web/src/nova/ed/panel/header/mid/add-text.tsx +++ b/app/web/src/nova/ed/panel/header/mid/add-text.tsx @@ -4,7 +4,7 @@ import { EDGlobal, active } from "../../../logic/ed-global"; import { getMetaById } from "../../../logic/tree/build"; import { createId } from "@paralleldrive/cuid2"; import { IText } from "../../../../../utils/types/text"; -import { MContent } from "../../../../../utils/types/general"; +import { IContent, MContent } from "../../../../../utils/types/general"; import { fillID } from "../../../logic/tree/fill-id"; import { prepSection } from "./prep-section"; import { IItem } from "../../../../../utils/types/item"; @@ -40,14 +40,15 @@ export const EdAddText = () => { let mitem = meta.mitem as MContent; if (mitem) { + const item = meta.item as IContent; if ( - meta.item.type === "text" || - (meta.item.type === "item" && meta.item.component?.id) + item.type === "text" || + (item.type === "item" && item.component?.id) ) { - const parent_id = meta.parent_item.id; + const parent_id = meta.parent?.id || "root"; const parent = getMetaById( p, - parent_id === "root" ? meta.item.id : parent_id + parent_id === "root" ? item.id : parent_id ); if (!parent) { alert("Failed to add text!"); diff --git a/app/web/src/nova/ed/panel/main/main.tsx b/app/web/src/nova/ed/panel/main/main.tsx index e96c64b7..08729c79 100644 --- a/app/web/src/nova/ed/panel/main/main.tsx +++ b/app/web/src/nova/ed/panel/main/main.tsx @@ -1,4 +1,5 @@ import { useGlobal, useLocal } from "web-utils"; +import { IContent } from "../../../../utils/types/general"; import { Vi } from "../../../vi/vi"; import { EDGlobal, active } from "../../logic/ed-global"; @@ -17,34 +18,35 @@ export const EdMain = () => { ` )} > -
+
{ + if ( + (meta.item as IContent).type === "text" && + !meta.item.adv?.jsBuilt + ) { + parts.props.spellCheck = false; + parts.props.contentEditable = true; + parts.props.dangerouslySetInnerHTML = { + __html: meta.mitem?.get("html") || "", + }; + parts.props.onBlur = (e) => { + e.stopPropagation(); + const mitem = meta.mitem; + if (mitem) { + mitem.set("html", e.currentTarget.innerHTML); + } + }; + } + parts.props.className = cx( parts.props.className, active.item_id === meta.item.id && @@ -86,6 +88,27 @@ export const EdMain = () => { ); }; +const mainStyle = () => { + return cx( + "absolute inset-0 flex", + active.hover.id && + css` + .s-${active.hover.id} { + &::after { + content: " "; + pointer-events: none; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + border: 2px solid #73b8ff; + } + } + ` + ); +}; + function setEndOfContenteditable(div: any) { let range: any, sel: any; if (document.createRange) { diff --git a/app/web/src/nova/ed/panel/popup/script/monaco.tsx b/app/web/src/nova/ed/panel/popup/script/monaco.tsx index 5720b4b7..42d7b3cb 100644 --- a/app/web/src/nova/ed/panel/popup/script/monaco.tsx +++ b/app/web/src/nova/ed/panel/popup/script/monaco.tsx @@ -88,7 +88,7 @@ export const EdScriptMonaco: FC<{}> = () => { { site_dts: p.site_dts, script: { - siteTypes: {}, + siteTypes: p.script.site_types, }, site: p.site.config, }, diff --git a/app/web/src/nova/ed/panel/popup/script/pop-script.tsx b/app/web/src/nova/ed/panel/popup/script/pop-script.tsx index 6d0f5d70..0400fb5a 100644 --- a/app/web/src/nova/ed/panel/popup/script/pop-script.tsx +++ b/app/web/src/nova/ed/panel/popup/script/pop-script.tsx @@ -2,9 +2,10 @@ import { useGlobal } from "web-utils"; import { jscript } from "../../../../../utils/script/jscript"; import { Loading } from "../../../../../utils/ui/loading"; import { Modal } from "../../../../../utils/ui/modal"; -import { EDGlobal } from "../../../logic/ed-global"; +import { EDGlobal, active } from "../../../logic/ed-global"; import { propPopover } from "../../side/prop-master/prop-form"; import { EdScriptWorkbench } from "./workbench"; +import { treeRebuild } from "../../../logic/tree/build"; export const EdPopScript = () => { const p = useGlobal(EDGlobal, "EDITOR"); @@ -14,16 +15,21 @@ export const EdPopScript = () => { open={p.ui.popup.script.open} onOpenChange={(open) => { if (!open) { - p.ui.popup.script.open = false; + const script = p.ui.popup.script; + if (script) { + script.open = false; - if ( - p.ui.popup.script.prop_name && - p.ui.popup.script.type === "prop-master" - ) { - propPopover.name = p.ui.popup.script.prop_name; + if (script.prop_name && script.type === "prop-master") { + propPopover.name = script.prop_name; + } + + if (script.type === "item") { + delete p.script.init_local_effect[active.item_id]; + treeRebuild(p, { note: "script-close" }); + } + + p.render(); } - - p.render(); } }} > diff --git a/app/web/src/nova/ed/panel/popup/script/scope.tsx b/app/web/src/nova/ed/panel/popup/script/scope.tsx index f6700634..091f3461 100644 --- a/app/web/src/nova/ed/panel/popup/script/scope.tsx +++ b/app/web/src/nova/ed/panel/popup/script/scope.tsx @@ -33,7 +33,8 @@ export const declareScope = async ( for (const m of parents) { if (active.comp_id && m.parent?.id === "root" && active.instance) { const meta = p.page.meta[active.instance.item_id]; - console.log(meta.scope.val); + if (!m.scope.def) m.scope.def = {}; + m.scope.def.props = meta.scope?.def?.props; } const def = m.scope.def; @@ -48,6 +49,23 @@ declare global { const ${def.local.name} = ${def.local.value}; }`, }); + } else if (def.props) { + Object.keys(def.props).map((e) => { + addScope({ + monaco, + loc: { + item_id: m.item.id, + type: "prop", + comp_id: m.parent?.comp_id, + prop_name: e, + }, + source: `\ +export const {}; +declare global { + const ${e} = null as any; +}`, + }); + }); } } } @@ -55,7 +73,12 @@ declare global { const addScope = (arg: { monaco: Monaco; - loc: { item_id: string; type: "prop" | "item" }; + loc: { + item_id: string; + type: "prop" | "item"; + comp_id?: string; + prop_name?: string; + }; source: string; }) => { const { monaco, source } = arg; diff --git a/app/web/src/nova/ed/panel/popup/script/workbench.tsx b/app/web/src/nova/ed/panel/popup/script/workbench.tsx index 0c36f64c..0300437c 100644 --- a/app/web/src/nova/ed/panel/popup/script/workbench.tsx +++ b/app/web/src/nova/ed/panel/popup/script/workbench.tsx @@ -1,7 +1,7 @@ import { useGlobal } from "web-utils"; -import { EdScriptMonaco } from "./monaco"; -import { EDGlobal, active } from "../../../logic/ed-global"; import { IItem } from "../../../../../utils/types/item"; +import { EDGlobal, active } from "../../../logic/ed-global"; +import { EdScriptMonaco } from "./monaco"; import { EdScriptSnippet } from "./snippet"; export const EdScriptWorkbench = () => { @@ -10,9 +10,9 @@ export const EdScriptWorkbench = () => {
- {p.ui.popup.script.type === "prop-master" ? ( - - ) : ( + {p.ui.popup.script.type === "prop-master" && } + {p.ui.popup.script.type === "prop-instance" && } + {p.ui.popup.script.type === "item" && ( <>
{[ @@ -46,7 +46,8 @@ export const EdScriptWorkbench = () => { ); })}
- {p.ui.popup.script.mode === "js" && } + {p.ui.popup.script.mode === "js" && + p.ui.popup.script.type === "item" && } )}
@@ -58,7 +59,27 @@ export const EdScriptWorkbench = () => { ); }; -const CompTitle = () => { +const CompTitleInstance = () => { + const p = useGlobal(EDGlobal, "EDITOR"); + + const item = p.page.meta[active.item_id].item as IItem; + + if (item && item.component?.id) { + const props = item.component.props; + return ( +
+
{item.name}
+ +
{p.ui.popup.script.prop_name}
+ +
{p.ui.popup.script.prop_kind}
+
+ ); + } + return <>; +}; + +const CompTitleMaster = () => { const p = useGlobal(EDGlobal, "EDITOR"); const item = p.comp.list[active.comp_id].doc @@ -69,7 +90,7 @@ const CompTitle = () => { if (item && item.component?.id) { const props = item.component.props; return ( -
+
{item.name}
{p.ui.popup.script.prop_name}
diff --git a/app/web/src/nova/ed/panel/tree/body.tsx b/app/web/src/nova/ed/panel/tree/body.tsx index 46182871..377c2c9b 100644 --- a/app/web/src/nova/ed/panel/tree/body.tsx +++ b/app/web/src/nova/ed/panel/tree/body.tsx @@ -20,7 +20,6 @@ export const EdTreeBody = () => { const TypedTree = DNDTree; active.hover.renderTree = local.render; - expandTreeHook(p, local); let tree: NodeModel[] = []; diff --git a/app/web/src/nova/ed/panel/tree/node/item/indent-hook.ts b/app/web/src/nova/ed/panel/tree/node/item/indent-hook.ts index eca01895..79e95cca 100644 --- a/app/web/src/nova/ed/panel/tree/node/item/indent-hook.ts +++ b/app/web/src/nova/ed/panel/tree/node/item/indent-hook.ts @@ -2,6 +2,7 @@ import { TreeMethods } from "@minoru/react-dnd-treeview"; import { useEffect } from "react"; import { IMeta, PG, active } from "../../../../logic/ed-global"; import { getMetaById } from "../../../../logic/tree/build"; +import { IContent } from "../../../../../../utils/types/general"; export const expandTreeHook = ( p: PG, @@ -21,14 +22,14 @@ export const expandTreeHook = ( ); const cur = getMetaById(p, active.item_id); - if (cur && cur.parent_item) { - const id = cur.parent_item.mitem?.get("id"); + if (cur && cur.parent?.id) { + const id = cur.parent.id; if (id) { shouldOpen.add(id); let meta: IMeta | undefined = getMetaById(p, id); while (meta) { - const id = meta.parent_item.id; + const id = meta.parent?.id; if (id && !shouldOpen.has(id)) { shouldOpen.add(id); meta = getMetaById(p, id); @@ -52,7 +53,7 @@ export const expandTreeHook = ( local.render(); if (active.item_id) { const meta = getMetaById(p, active.item_id); - if (meta && meta.item.type !== "text") { + if (meta && (meta.item as IContent).type !== "text") { setTimeout(() => { const el = document.getElementsByClassName(active.item_id); if (el.length > 0) { diff --git a/app/web/src/nova/vi/render/global.tsx b/app/web/src/nova/vi/render/global.tsx index 93e52275..b92c3e33 100644 --- a/app/web/src/nova/vi/render/global.tsx +++ b/app/web/src/nova/vi/render/global.tsx @@ -11,6 +11,9 @@ export const ViGlobal = { api: null as any, db: null as any, }, + script: { + init_local_effect: undefined as undefined | Record, + }, visit: undefined as | undefined | ((meta: IMeta, parts: ReturnType) => void), diff --git a/app/web/src/nova/vi/render/script/eval-prop.tsx b/app/web/src/nova/vi/render/script/eval-prop.tsx index f1de8cc3..1dd27388 100644 --- a/app/web/src/nova/vi/render/script/eval-prop.tsx +++ b/app/web/src/nova/vi/render/script/eval-prop.tsx @@ -35,7 +35,7 @@ export const viEvalProps = ( ` ); - meta.scope.def.props[name] = { name, value: prop.valueBuilt }; + meta.scope.def.props[name] = { value: prop.valueBuilt }; let val = fn(...Object.values(arg)); if (typeof val === "function") { @@ -58,7 +58,7 @@ export const viEvalProps = ( export const updatePropScope = (meta: IMeta, scope: any) => { if (meta.scope.def?.props) { - for (const prop of Object.values(meta.scope.def.props)) { + for (const [name, prop] of Object.entries(meta.scope.def.props)) { if (prop.fn) { const all_scope = { ...scope, @@ -66,7 +66,7 @@ export const updatePropScope = (meta: IMeta, scope: any) => { }; const fn = new Function( ...Object.keys(all_scope), - `// [${meta.item.name}] ${prop.name}: ${meta.item.id} + `// [${meta.item.name}] ${name}: ${meta.item.id} return ${prop.value || ""} ` ); diff --git a/app/web/src/nova/vi/render/script/eval-script.tsx b/app/web/src/nova/vi/render/script/eval-script.tsx index 4a5267fc..bb5c44d1 100644 --- a/app/web/src/nova/vi/render/script/eval-script.tsx +++ b/app/web/src/nova/vi/render/script/eval-script.tsx @@ -10,7 +10,11 @@ import { createViLocal } from "./local"; import { createViPassProp } from "./passprop"; export const viEvalScript = ( - vi: { meta: VG["meta"]; visit?: VG["visit"] }, + vi: { + meta: VG["meta"]; + visit?: VG["visit"]; + script?: { init_local_effect: any }; + }, meta: IMeta, scope: any ) => { @@ -30,7 +34,7 @@ export const viEvalScript = ( if (!meta.script) { meta.script = { result: null, - Local: createViLocal(meta, scope), + Local: createViLocal(meta, scope, vi.script?.init_local_effect), PassProp: createViPassProp(vi, meta, scope), }; } diff --git a/app/web/src/nova/vi/render/script/local.tsx b/app/web/src/nova/vi/render/script/local.tsx index 01fd5b93..9ed20782 100644 --- a/app/web/src/nova/vi/render/script/local.tsx +++ b/app/web/src/nova/vi/render/script/local.tsx @@ -2,7 +2,11 @@ import { ReactNode, useEffect, useRef, useState } from "react"; import { IMeta } from "../../../ed/logic/ed-global"; import { updatePropScope } from "./eval-prop"; -export const createViLocal = (meta: IMeta, scope: any) => { +export const createViLocal = ( + meta: IMeta, + scope: any, + init_local_effect: any +) => { return >(arg: { children: ReactNode; name: string; @@ -20,6 +24,7 @@ export const createViLocal = (meta: IMeta, scope: any) => { } const val = meta.scope.val; val[arg.name] = local; + updatePropScope(meta, scope); if (arg.hook) { @@ -27,13 +32,20 @@ export const createViLocal = (meta: IMeta, scope: any) => { } useEffect(() => { - const fn = async () => { - if (arg.effect) { - await arg.effect(local); + let should_run = !init_local_effect[meta.item.id]; + if (should_run) { + if (typeof init_local_effect === "object") { + init_local_effect[meta.item.id] = true; } - }; + const fn = async () => { + if (arg.effect) { + await arg.effect(local); + } + }; + + fn(); + } - fn(); return () => {}; }, []); diff --git a/app/web/src/nova/vi/utils/error-box.tsx b/app/web/src/nova/vi/utils/error-box.tsx index e75b5d98..9407f939 100644 --- a/app/web/src/nova/vi/utils/error-box.tsx +++ b/app/web/src/nova/vi/utils/error-box.tsx @@ -21,7 +21,7 @@ export const ErrorBox = withErrorBoundary( if (meta && local.meta !== meta) { local.meta = meta; - resetError(); + setTimeout(resetError); } let _meta = meta; diff --git a/app/web/src/nova/vi/vi.tsx b/app/web/src/nova/vi/vi.tsx index 8596eb47..67254ff7 100644 --- a/app/web/src/nova/vi/vi.tsx +++ b/app/web/src/nova/vi/vi.tsx @@ -5,21 +5,25 @@ import { viLoad } from "./load/load"; import { VG, ViGlobal } from "./render/global"; import { ViRoot } from "./root"; import { ErrorBox } from "./utils/error-box"; -import { viParts } from "./render/parts"; export const Vi: FC<{ meta: Record; entry: string[]; api_url: string; site_id: string; + page_id: string; api?: any; db?: any; + script?: { init_local_effect: Record }; visit?: VG["visit"]; -}> = ({ meta, entry, api_url, site_id, api, db, visit }) => { +}> = ({ meta, entry, api_url, site_id, api, db, visit, script }) => { const vi = useGlobal(ViGlobal, "VI"); if (vi.meta !== meta) { vi.meta = meta; } + if (script) { + vi.script.init_local_effect = script.init_local_effect; + } vi.visit = visit; if (vi.status === "init") { diff --git a/app/web/src/utils/script/mount.tsx b/app/web/src/utils/script/mount.tsx index cc2fd7d2..1d92045b 100644 --- a/app/web/src/utils/script/mount.tsx +++ b/app/web/src/utils/script/mount.tsx @@ -63,7 +63,7 @@ export const jsMount = async (editor: MonacoEditor, monaco: Monaco, p?: PG) => { type: "" as "prop" | "item", item_id: "" as string | undefined, comp_id: "", - instance_id: "", + prop_name: "", }; try { arg = JSON.parse(cpath.substring(0, cpath.length - 5) || "{}"); @@ -88,6 +88,12 @@ export const jsMount = async (editor: MonacoEditor, monaco: Monaco, p?: PG) => { p.render(); } } else if (arg.type === "prop") { + active.comp_id = arg.comp_id; + p.ui.popup.script.prop_name = arg.prop_name; + p.ui.popup.script.type = "prop-instance"; + p.ui.popup.script.prop_kind = "value"; + p.ui.popup.script.open = true; + p.render(); } }, 100); }