diff --git a/app/srv/ws/sync/entity/user.ts b/app/srv/ws/sync/entity/user.ts index f258ccea..4bbf1c31 100644 --- a/app/srv/ws/sync/entity/user.ts +++ b/app/srv/ws/sync/entity/user.ts @@ -16,6 +16,7 @@ export const user = { site_id: string; page_id: string; comp_id?: string; + client_id: string; select: "" | "comp" | "item" | "section" | "text"; }, "client_id" diff --git a/app/web/package.json b/app/web/package.json index f71dff51..7a50be9e 100644 --- a/app/web/package.json +++ b/app/web/package.json @@ -12,6 +12,7 @@ "@minoru/react-dnd-treeview": "^3.4.4", "@monaco-editor/react": "^4.6.0", "@paralleldrive/cuid2": "2.2.2", + "react-contenteditable": "^3.3.7", "@parcel/packager-wasm": "^2.10.3", "@parcel/service-worker": "^2.10.3", "recast": "^0.23.4", diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index 5b2c0885..d3310ec3 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -64,7 +64,7 @@ const target = { }; export const active = { hover_id: "", - text: { id: "", content: "" }, + text: { id: "", content: "", timeout: null as any, el: null as any }, get item_id() { if (target.active_id === false) { target.active_id = localStorage.getItem("prasi-active-id") || ""; diff --git a/app/web/src/nova/ed/panel/main/main.tsx b/app/web/src/nova/ed/panel/main/main.tsx index dcddd623..6742d4ae 100644 --- a/app/web/src/nova/ed/panel/main/main.tsx +++ b/app/web/src/nova/ed/panel/main/main.tsx @@ -1,10 +1,11 @@ -import { useGlobal } from "web-utils"; +import { useGlobal, useLocal } from "web-utils"; import { Loading } from "../../../../utils/ui/loading"; import { View } from "../../../view/view"; import { EDGlobal, active } from "../../logic/ed-global"; import { getMetaById } from "../../logic/tree/build"; import { loadComponent } from "../../logic/tree/sync-walk"; import { code } from "../popup/code/code"; +import { useEffect } from "react"; export const EdMain = () => { const p = useGlobal(EDGlobal, "EDITOR"); @@ -51,10 +52,11 @@ export const EdMain = () => { const item = meta.item; if (active.comp_id) { - if (meta.parent_mcomp) { + const p = meta.parent_mcomp; + if (p) { if ( - meta.parent_mcomp?.mcomp.get("component")?.get("id") !== - active.comp_id + p.mitem.get("id") !== active.instance.item_id || + p.mcomp.get("component")?.get("id") !== active.comp_id ) return false; } @@ -112,15 +114,16 @@ export const EdMain = () => { const item = meta.item; if (active.comp_id) { - if (meta.parent_mcomp) { + const p = meta.parent_mcomp; + if (p) { if ( - meta.parent_mcomp?.mcomp.get("component")?.get("id") !== - active.comp_id + p.mitem.get("id") !== active.instance.item_id || + p.mcomp.get("component")?.get("id") !== active.comp_id ) return false; } } - + if ( item.originalId === active.item_id || item.id === active.item_id @@ -194,46 +197,74 @@ export const EdMain = () => { p.page.render(); focus(); }, - text(meta) { + text({ meta }) { const { item } = meta; + + useEffect(() => { + return () => { + active.text.id = ""; + p.render(); + }; + }, []); + + const updateWithTimeout = (timeout: number) => { + return new Promise((resolve) => { + const saving = { + id: active.text.id, + content: active.text.content, + }; + + clearTimeout(active.text.timeout); + active.text.timeout = setTimeout(() => { + const meta = getMetaById(p, saving.id); + if (meta && meta.mitem) { + meta.mitem.set("html", saving.content); + } + resolve(); + }, timeout); + }); + }; + if (active.text.id !== item.id) { + clearTimeout(active.text.timeout); active.text.id = item.id; active.text.content = item.html || ""; + active.text.el = ( +
{ + if (ref !== document.activeElement && ref) { + const renaming = + document.querySelector(".rename-item"); + const modals = document.querySelectorAll( + "[data-floating-ui-portal]" + ); + if (modals.length === 0 && !renaming) { + ref.focus(); + setEndOfContenteditable(ref); + } + } + }} + onPointerDownCapture={(e) => { + e.stopPropagation(); + }} + contentEditable + spellCheck={false} + onInput={(e) => { + const val = e.currentTarget.innerHTML; + item.html = val; + active.text.id = item.id; + active.text.content = val; + updateWithTimeout(100); + }} + dangerouslySetInnerHTML={{ __html: item.html || "" }} + >
+ ); } - return ( -
{ - if (ref !== document.activeElement && ref) { - const renaming = document.querySelector(".rename-item"); - const modals = document.querySelectorAll( - "[data-floating-ui-portal]" - ); - if (modals.length === 0 && !renaming) { - ref.focus(); - setEndOfContenteditable(ref); - } - } - }} - contentEditable - spellCheck={false} - onInput={(e) => { - const val = e.currentTarget.innerHTML; - active.text.id = item.id; - active.text.content = val; - }} - onBlur={() => { - const meta = getMetaById(p, item.id); - if (meta && meta.mitem && active.text.id === item.id) { - meta.mitem.set("html", active.text.content); - } - }} - dangerouslySetInnerHTML={{ __html: item.html || "" }} - >
- ); + return active.text.el; }, }} /> diff --git a/app/web/src/nova/ed/panel/side/prop-instance/prop-text.tsx b/app/web/src/nova/ed/panel/side/prop-instance/prop-text.tsx index f11584c4..c69d4909 100644 --- a/app/web/src/nova/ed/panel/side/prop-instance/prop-text.tsx +++ b/app/web/src/nova/ed/panel/side/prop-instance/prop-text.tsx @@ -29,7 +29,7 @@ export const EdPropInstanceText: FC<{ if (val) { try { eval(`local.value = ${valBuilt}`); - } catch (e) { } + } catch (e) {} } else { local.value = ""; } @@ -41,7 +41,7 @@ export const EdPropInstanceText: FC<{ {name} ); - + return (
{name.length > 8 ? ( @@ -52,7 +52,7 @@ export const EdPropInstanceText: FC<{ label )} { diff --git a/app/web/src/nova/view/logic/global.ts b/app/web/src/nova/view/logic/global.ts index b069fce2..ce2c94f1 100644 --- a/app/web/src/nova/view/logic/global.ts +++ b/app/web/src/nova/view/logic/global.ts @@ -25,7 +25,7 @@ export const ViewGlobal = { | { get: (meta: EdMeta) => boolean; set: (meta: EdMeta) => void; - text?: (meta: EdMeta) => ReactNode; + text?: (arg: { meta: EdMeta }) => ReactNode; }, hover: undefined as | undefined diff --git a/app/web/src/nova/view/render/meta/children.tsx b/app/web/src/nova/view/render/meta/children.tsx index 67302210..5abdf692 100644 --- a/app/web/src/nova/view/render/meta/children.tsx +++ b/app/web/src/nova/view/render/meta/children.tsx @@ -1,42 +1,48 @@ import { FC, Fragment, ReactNode } from "react"; -import { useGlobal } from "web-utils"; +import { useGlobal, useLocal } from "web-utils"; import { EdMeta } from "../../../ed/logic/ed-global"; import { ViewGlobal } from "../../logic/global"; import { ViewMeta } from "./meta"; export const ViewMetaChildren: FC<{ - meta: EdMeta, - className?: string -}> = ({ - meta, - className -}) => { - const v = useGlobal(ViewGlobal, "VIEW"); - const children: Record = {}; - const item = meta.item; - if (item.type !== "text") { - for (const child of item.childs) { - if (child.id) { - children[child.id] = (); - } - } - } else { - if (item.id) { - if (v.view.active?.text && v.view.active?.get(meta)) { - children[item.id] = - {v.view.active.text(meta)} - ; - } else { - children[item.id] = - ; - } + meta: EdMeta; + className?: string; +}> = ({ meta, className }) => { + const v = useGlobal(ViewGlobal, "VIEW"); + const children: Record = {}; + const item = meta.item; + if (item.type !== "text") { + for (const child of item.childs) { + if (child.id) { + children[child.id] = ; } } + } else { + if (item.id) { + if (v.view.active?.text && v.view.active?.get(meta)) { + const ActiveText = v.view.active.text; + children[item.id] = ( + + + + ); + } else { + children[item.id] = ( + + ); + } + } + } - return <> - {/*
{item.id} - {item.originalId}
*/} - {Object.values(children)}; - }; + return ( + <> + {/*
+ {item.id} - {item.originalId} +
*/} + {Object.values(children)} + + ); +}; diff --git a/app/web/src/nova/view/view.tsx b/app/web/src/nova/view/view.tsx index 42578ef2..0beca60f 100644 --- a/app/web/src/nova/view/view.tsx +++ b/app/web/src/nova/view/view.tsx @@ -26,7 +26,7 @@ type ViewProp = { active?: { get: (item: EdMeta) => boolean; set: (item: EdMeta) => void; - text?: (item: EdMeta) => ReactNode + text?: (arg: { meta: EdMeta }) => ReactNode; }; }; @@ -59,8 +59,9 @@ const BoxedView: FC = ({ v.script.api_url = api_url; if (hidden) v.view.hidden = hidden; - if (hover) v.view.hover = hover; - if (active) v.view.active = active; + if (hover && !v.view.hover) v.view.hover = hover; + if (active && !v.view.active) v.view.active = active; + if (v.current.page_id !== page_id || v.current.site_id !== site_id) { v.status = "init"; } diff --git a/bun.lockb b/bun.lockb index cc191e81..f561bbb1 100755 Binary files a/bun.lockb and b/bun.lockb differ