diff --git a/app/srv/ws/sync/actions/code_edit.ts b/app/srv/ws/sync/actions/code_edit.ts index 45051660..c2127aee 100644 --- a/app/srv/ws/sync/actions/code_edit.ts +++ b/app/srv/ws/sync/actions/code_edit.ts @@ -175,6 +175,9 @@ export const code_edit: SAction["code"]["edit"] = async function ( if (prop_kind === "value") { mprop.set("value", src); mprop.set("valueBuilt", res.code.substring(6)); + } else if (prop_kind === "onChange") { + mprop.set("onChange", src); + mprop.set("onChangeBuilt", res.code.substring(6)); } else if (prop_kind === "gen") { mprop.set("gen", src); mprop.set("genBuilt", res.code.substring(6)); diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index eabca8b3..e0a8d0c4 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -29,7 +29,13 @@ export const EmptySite = { export type ESite = typeof EmptySite; export type EPage = typeof EmptyPage; export type EComp = typeof EmptyComp; -export type PropFieldKind = "visible" | "gen" | "value" | "option" | "typings"; +export type PropFieldKind = + | "onChange" + | "visible" + | "gen" + | "value" + | "option" + | "typings"; export type ISingleScope = { p: string[]; n: string; diff --git a/app/web/src/nova/ed/panel/popup/script/default-val.tsx b/app/web/src/nova/ed/panel/popup/script/default-val.tsx index 9df7500b..d1b2e037 100644 --- a/app/web/src/nova/ed/panel/popup/script/default-val.tsx +++ b/app/web/src/nova/ed/panel/popup/script/default-val.tsx @@ -38,6 +38,13 @@ export const edMonacoDefaultVal = (p: PG, adv: FNAdv, mitem: MItem) => { if (kind === "value") { val = mprop.get("value"); + } else if (kind === "onChange") { + val = + mprop.get("onChange") || + `\ +({ name, value, item }: { name: string; value: string; item: PrasiItem }) => { + // on prop changed +}`; } else if (kind === "gen") { val = mprop.get("gen") || 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 d64f659e..8c849466 100644 --- a/app/web/src/nova/ed/panel/popup/script/monaco.tsx +++ b/app/web/src/nova/ed/panel/popup/script/monaco.tsx @@ -17,6 +17,7 @@ import { declareScope } from "./scope/scope"; // @ts-ignore import { FNCompDef } from "../../../../../utils/types/meta-fn"; import { editorLocalValue } from "../../../../vi/render/script/local"; +import { propInstanceOnChange } from "../../side/prop-instance/on-change"; const scriptEdit = { timeout: null as any, @@ -342,6 +343,11 @@ export const EdScriptMonaco: FC<{}> = () => { value: compress(encode.encode(value || "")), ...arg, }); + + if (p.ui.popup.script.prop_kind === "value") { + propInstanceOnChange(p, p.ui.popup.script.prop_name, value); + } + if (typeof code_result === "string") { p.ui.popup.script.typings.status = "error"; p.ui.popup.script.typings.err_msg = code_result; diff --git a/app/web/src/nova/ed/panel/side/prop-instance/on-change.tsx b/app/web/src/nova/ed/panel/side/prop-instance/on-change.tsx new file mode 100644 index 00000000..40ebbc70 --- /dev/null +++ b/app/web/src/nova/ed/panel/side/prop-instance/on-change.tsx @@ -0,0 +1,35 @@ +import { IItem } from "../../../../../utils/types/item"; +import { FNComponent } from "../../../../../utils/types/meta-fn"; +import { devItem, PrasiEdit } from "../../../../vi/render/script/item-dev"; +import { getActiveMeta } from "../../../logic/active/get-meta"; +import { PG } from "../../../logic/ed-global"; + +export const propInstanceOnChange = (p: PG, name: string, value: any) => { + const meta = getActiveMeta(p); + + if (meta && meta.item.component) { + const comp = p.comp.list[meta.item.component.id]?.doc + .getMap("map") + .get("root") + ?.get("component") + ?.toJSON() as FNComponent; + + if (comp) { + const prop = comp.props[name]; + if (prop && prop.onChangeBuilt && meta.mitem) { + const gen_fn = new Function(`return ${prop.onChangeBuilt}`); + const fn = gen_fn() as (arg: { + name: string; + value: any; + item: IItem & PrasiEdit; + }) => void; + + fn({ + name, + value, + item: devItem(p.page.meta, meta.mitem, p.page.cur.id), + }); + } + } + } +}; diff --git a/app/web/src/nova/ed/panel/side/prop-instance/prop-code.tsx b/app/web/src/nova/ed/panel/side/prop-instance/prop-code.tsx index 7c8ef086..4c24e773 100644 --- a/app/web/src/nova/ed/panel/side/prop-instance/prop-code.tsx +++ b/app/web/src/nova/ed/panel/side/prop-instance/prop-code.tsx @@ -1,8 +1,8 @@ import { FC } from "react"; -import { FMCompDef } from "../../../../../utils/types/meta-fn"; -import { EdPropLabel } from "./prop-label"; import { useGlobal } from "web-utils"; -import { EDGlobal, active } from "../../../logic/ed-global"; +import { FMCompDef } from "../../../../../utils/types/meta-fn"; +import { EDGlobal } from "../../../logic/ed-global"; +import { EdPropLabel } from "./prop-label"; import { reset } from "./prop-reset"; export const EdPropInstanceCode: FC<{ diff --git a/app/web/src/nova/ed/panel/side/prop-instance/prop-option.tsx b/app/web/src/nova/ed/panel/side/prop-instance/prop-option.tsx index 19e74673..aabc82c7 100644 --- a/app/web/src/nova/ed/panel/side/prop-instance/prop-option.tsx +++ b/app/web/src/nova/ed/panel/side/prop-instance/prop-option.tsx @@ -8,6 +8,7 @@ import { treeRebuild } from "../../../logic/tree/build"; import { EdPropLabel } from "./prop-label"; import { ChevronDown } from "../../tree/node/item/indent"; import { Popover } from "../../../../../utils/ui/popover"; +import { propInstanceOnChange } from "./on-change"; type MetaOption = { label: string; @@ -45,6 +46,7 @@ export const EdPropInstanceOptions: FC<{ resetOnDeps: false as boolean | (() => any[]), open: false, pendingVal: null as any, + changedTimeout: null as any, }); const p = useGlobal(EDGlobal, "EDITOR"); @@ -231,6 +233,11 @@ export const EdPropInstanceOptions: FC<{ treeRebuild(p); p.render(); + clearTimeout(local.changedTimeout); + local.changedTimeout = setTimeout(() => { + propInstanceOnChange(p, name, val); + }, 1000); + setTimeout(() => { if (item?.reload) { for (const name of item.reload) { 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 a4a5f48a..e8a3bde0 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 @@ -10,6 +10,7 @@ import { FMCompDef } from "../../../../../utils/types/meta-fn"; import { EdPropLabel } from "./prop-label"; import { treeRebuild } from "../../../logic/tree/build"; import { EDGlobal } from "../../../logic/ed-global"; +import { propInstanceOnChange } from "./on-change"; export const EdPropInstanceText: FC<{ name: string; @@ -25,6 +26,7 @@ export const EdPropInstanceText: FC<{ codeEditing: false, timeout: null as any, focus: false, + changedTimeout: null as any, }); useEffect(() => { @@ -44,26 +46,26 @@ export const EdPropInstanceText: FC<{ dragnum={ typeof valnum === "number" && !isNaN(valnum) ? { - value: valnum, - onChange(value) { - local.value = Math.round(value) + ""; - local.render(); - }, - onChanged(value) { - local.value = Math.round(value) + ""; - local.render(); + value: valnum, + onChange(value) { + local.value = Math.round(value) + ""; + local.render(); + }, + onChanged(value) { + local.value = Math.round(value) + ""; + local.render(); - clearTimeout(local.timeout); - local.timeout = setTimeout(() => { - mprop.doc?.transact(() => { - mprop.set("value", `\`${local.value}\``); - mprop.set("valueBuilt", `\`${local.value}\``); - }); - treeRebuild(p); - p.render(); - }, 1000); - }, - } + clearTimeout(local.timeout); + local.timeout = setTimeout(() => { + mprop.doc?.transact(() => { + mprop.set("value", `\`${local.value}\``); + mprop.set("valueBuilt", `\`${local.value}\``); + }); + treeRebuild(p); + p.render(); + }, 1000); + }, + } : undefined } /> @@ -84,6 +86,8 @@ export const EdPropInstanceText: FC<{ local.value = e.currentTarget.value; local.render(); clearTimeout(local.timeout); + clearTimeout(local.changedTimeout); + local.timeout = setTimeout(() => { mprop.doc?.transact(() => { mprop.set("value", `\`${local.value}\``); @@ -91,6 +95,11 @@ export const EdPropInstanceText: FC<{ }); treeRebuild(p); p.render(); + + clearTimeout(local.changedTimeout); + local.changedTimeout = setTimeout(() => { + propInstanceOnChange(p, name, `\`${local.value}\``); + }, 500); }, 200); }} /> diff --git a/app/web/src/nova/ed/panel/side/prop-master/prop-form.tsx b/app/web/src/nova/ed/panel/side/prop-master/prop-form.tsx index c2841dfd..695f9d90 100644 --- a/app/web/src/nova/ed/panel/side/prop-master/prop-form.tsx +++ b/app/web/src/nova/ed/panel/side/prop-master/prop-form.tsx @@ -329,6 +329,16 @@ export const EdPropPopoverForm: FC<{ )} +
+
ON CHANGE
+
+ EDIT CODE +
+
+ {type === "option" && (
MODE
diff --git a/app/web/src/utils/script/types/base.ts b/app/web/src/utils/script/types/base.ts index 17a2cc3b..375090ef 100644 --- a/app/web/src/utils/script/types/base.ts +++ b/app/web/src/utils/script/types/base.ts @@ -74,7 +74,7 @@ export const baseTypings = ` }; childs: IItem[]; }; - + type SingleChange = | { type: "set"; name: string; value: any } | ({ type: "prop"; name: string } & PropVal) @@ -92,23 +92,23 @@ export const baseTypings = ` }; type SimpleItem = Partial> & { - component?: { id: string; props: Record }; + component?: { id: string; props: Record }; }; - export type PrasiEdit = { + type PrasiEdit = { edit: { setValue: (name: T, value: IItem[T]) => void; setProp: (name: string, value: PropVal | string) => void; pending: SingleChange[]; childs: (IItem & PrasiEdit)[]; setChilds: (childs: ((IItem & PrasiEdit) | SimpleItem)[]) => void; - readonly parent: null | ParentArg; + readonly parent: null | ParentArg; commit: () => Promise; readonly props?: Record; }; }; - const _item: undefined | PrasiItem; + type PrasiItem = IItem & PrasiEdit; const PassProp: (arg:Record & { children: ReactNode }>) => ReactElement; const mobile: { diff --git a/app/web/src/utils/types/meta-fn.ts b/app/web/src/utils/types/meta-fn.ts index dfea73ba..06da431b 100644 --- a/app/web/src/utils/types/meta-fn.ts +++ b/app/web/src/utils/types/meta-fn.ts @@ -36,6 +36,8 @@ export type FNCompDef = { gen?: string; genBuilt?: string; is_name?: boolean; + onChange?: string; + onChangeBuilt?: string; jsxCalledBy?: string[]; content?: IItem; visible?: string;