wip remove view

This commit is contained in:
Rizky 2023-12-14 02:09:50 +07:00
parent 8bdc357d7f
commit 8179e3f74c
26 changed files with 7 additions and 1612 deletions

View File

@ -8,9 +8,9 @@ import { IItem } from "../../../utils/types/item";
import { DComp, DPage } from "../../../utils/types/root"; import { DComp, DPage } from "../../../utils/types/root";
import { import {
GenMetaP, GenMetaP,
IMeta as LogicMeta,
ISimpleMeta, ISimpleMeta,
} from "../../view/logic/meta/types"; IMeta as LogicMeta,
} from "../../vi/utils/types";
export type IMeta = LogicMeta; export type IMeta = LogicMeta;

View File

@ -1,9 +1,9 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { parseJs } from "../../../../../../srv/ws/sync/editor/parser/parse-js"; import { parseJs } from "../../../../../srv/ws/sync/editor/parser/parse-js";
import { IContent } from "../../../../utils/types/general"; import { IContent } from "../../../utils/types/general";
import { IItem, MItem } from "../../../../utils/types/item"; import { IItem, MItem } from "../../../utils/types/item";
import { createViLocal } from "../../../vi/render/script/local"; import { createViLocal } from "../render/script/local";
import { createViPassProp } from "../../../vi/render/script/passprop"; import { createViPassProp } from "../render/script/passprop";
export type GenMetaP = { export type GenMetaP = {
meta: Record<string, IMeta>; meta: Record<string, IMeta>;

View File

@ -1,36 +0,0 @@
import { ReactElement, ReactNode } from "react";
import { IMeta, IScope } from "../../ed/logic/ed-global";
export const ViewGlobal = {
mode: "" as "desktop" | "mobile",
status: "init" as "init" | "load-code" | "loading-code" | "ready" | "rebuild",
current: { site_id: "", page_id: "" },
layout: { show: false },
meta: {} as Record<string, IMeta>,
scope: null as null | IScope,
entry: [] as string[],
body_cache: null as null | ReactElement,
component: {
load: async (id_comp: string) => {},
},
script: {
api_url: "",
db: null as any,
api: null as any,
},
view: {
hidden: undefined as undefined | ((meta: IMeta) => boolean),
active: undefined as
| undefined
| {
get: (meta: IMeta) => boolean;
set: (meta: IMeta) => void;
text?: (arg: { meta: IMeta }) => ReactNode;
},
hover: undefined as
| undefined
| { get: (meta: IMeta) => boolean; set: (meta: IMeta) => void },
},
};
export type VG = typeof ViewGlobal & { render: () => void };

View File

@ -1,35 +0,0 @@
import { VG } from "./global";
import { VLoad } from "./types";
export const w = window as unknown as {
isMobile: boolean;
isDesktop: boolean;
isEditor: boolean;
};
export const vInit = (
v: VG,
arg: {
load: VLoad;
site_id: string;
page_id: string;
mode: "mobile" | "desktop";
isEditor: boolean;
}
) => {
const { load, site_id, page_id, mode, isEditor } = arg;
w.isDesktop = mode !== "mobile";
w.isMobile = mode === "mobile";
w.isEditor = isEditor;
v.status = "load-code";
v.current.site_id = site_id;
v.current.page_id = page_id;
if (load.mode === "tree_meta") {
v.meta = load.meta;
v.entry = load.entry;
v.scope = load.scope || null;
}
};

View File

@ -1,130 +0,0 @@
import { VG } from "./global";
export const codeLoaded = new Set<string>();
const codeMap = {
page: {} as Record<string, string[]>,
compGroup: {} as Record<string, string[]>,
comp: {} as Record<string, string>,
};
export const newLoadCode = async (v: VG, forceLoad?: boolean) => {
if (forceLoad) {
codeLoaded.clear();
v.status = "load-code";
v.render();
}
if (v.status === "load-code") {
v.status = "loading-code";
const { site_id, page_id } = v.current;
const w = window as any;
if (!codeLoaded.has(site_id)) {
codeLoaded.add(site_id);
const module = await importCJS(`/nova-load/site/${site_id}/index.js`);
for (const [k, v] of Object.entries(module)) {
w[k] = v;
}
const code = await db.code.findMany({
where: { id_site: site_id, name: { notIn: ["site", "SSR"] } },
select: { code_assign: true },
});
codeMap.compGroup = {};
codeMap.page = {};
for (const c of code) {
c.code_assign.forEach((e) => {
if (e.id_page) {
if (!codeMap.page[e.id_page]) codeMap.page[e.id_page] = [];
codeMap.page[e.id_page].push(e.id_code);
}
if (e.id_component_group) {
if (!codeMap.compGroup[e.id_component_group])
codeMap.compGroup[e.id_component_group] = [];
codeMap.page[e.id_component_group].push(e.id_code);
}
});
}
codeMap.comp = {};
const comps = await db.component.findMany({
where: { id_component_group: { in: Object.keys(codeMap.compGroup) } },
select: { id: true, id_component_group: true },
});
for (const c of comps) {
if (c.id && c.id_component_group) {
codeMap.comp[c.id] = c.id_component_group;
}
}
}
const promises = [];
if (codeMap.page[page_id]) {
for (const id of codeMap.page[page_id]) {
promises.push(
new Promise<void>(async (resolve) => {
if (!codeLoaded.has(id)) {
codeLoaded.add(id);
const module = await importCJS(`/nova-load/code/${id}/index.js`);
for (const [k, v] of Object.entries(module)) {
w[k] = v;
}
}
resolve();
})
);
}
}
await Promise.all(promises);
v.status = "rebuild";
v.render();
}
};
export const loadCompCode = async (comp_id: string) => {
const cgroup_id = codeMap.comp[comp_id];
if (codeMap.compGroup[cgroup_id]) {
const promises = [];
const w = window as any;
for (const id of codeMap.compGroup[cgroup_id]) {
promises.push(
new Promise<void>(async (resolve) => {
if (!codeLoaded.has(id)) {
codeLoaded.add(id);
const module = await importCJS(`/nova-load/code/${id}/index.js`);
for (const [k, v] of Object.entries(module)) {
w[k] = v;
}
}
resolve();
})
);
}
await Promise.all(promises);
}
};
const importCJS = async (url: string) => {
const res = await fetch(url);
const src = await res.text();
return evalCJS(src);
};
export const evalCJS = (src: string) => {
if (src) {
const module = { exports: { __esModule: true as true | undefined } };
eval(`try {
${src}
} catch(e) {
console.error(e);
}`);
const result = { ...module.exports };
if (result.__esModule) {
delete result.__esModule;
}
return result;
}
return {};
};

View File

@ -1,59 +0,0 @@
import importModule from "../../../render/editor/tools/dynamic-import";
import { devLoader } from "../../../render/live/dev-loader";
import { createAPI, createDB, initApi } from "../../../utils/script/init-api";
import { VG } from "./global";
export const oldLoadCode = async (v: VG) => {
const site = await db.site.findFirst({
where: { id: v.current.site_id },
include: { component_site: true },
});
const loader = devLoader;
const p = {} as any;
if (site) {
const w = window as any;
if (!w.exports) w.exports = {};
if (site.component_site) {
for (const cg of site.component_site) {
await importModule(loader.npm(p, "site", cg.id_component_group));
}
}
await initApi(site.config);
await importModule(loader.npm(p, "site", site.id));
if (site.js_compiled) {
const config = site.config as any;
const exec = (fn: string, scopes: any) => {
if (config.api_url && !scopes["api"]) {
scopes["api"] = createAPI(config.api_url);
scopes["db"] = createDB(config.api_url);
}
scopes.params = w.params;
scopes.module = {};
const f = new Function(...Object.keys(scopes), fn);
const res = f(...Object.values(scopes));
return res;
};
const scope = {
types: {},
exports: w.exports,
load: importModule,
render: p.render,
module: {
exports: {} as any,
},
};
await exec(site.js_compiled, scope);
if (scope.module.exports) {
for (const [k, v] of Object.entries(scope.module.exports)) {
w.exports[k] = v;
}
}
}
}
v.status = "rebuild";
v.render();
};

View File

@ -1,136 +0,0 @@
import { instantiate, walkChild } from "./comp/instantiate";
import { walkProp } from "./comp/walk-prop";
import { genMeta } from "./meta";
import { simplifyItemChild } from "./simplify";
import { GenMetaArg, GenMetaP, IMeta } from "./types";
export const genComp = (p: GenMetaP, arg: GenMetaArg) => {
const { item } = arg;
if (item.type === "item" && item.component?.id && arg.parent?.item.id) {
let pcomp = p.comps[item.component.id];
if (p.on?.visit_component) {
p.on.visit_component(item.component.id);
}
if (!pcomp) {
return;
}
if (pcomp) {
let smeta_instances: IMeta["instances"] = undefined;
if (p.smeta && p.smeta[item.id]) {
const smeta = p.smeta[item.id];
if (smeta && smeta.comp) {
smeta_instances = smeta.comp.instances;
}
}
let instance = {};
let instances: IMeta["instances"] = undefined;
if (!smeta_instances) {
const parent_instance = getParentInstance(p, arg, item.id);
instance = parent_instance || {};
instances = !parent_instance ? { [item.id]: instance } : undefined;
} else {
instance = smeta_instances[item.id];
instances = smeta_instances;
}
instantiate({
item,
comp: pcomp.comp,
ids: instance,
});
const meta: IMeta = {
item: simplifyItemChild(item),
parent: {
id: arg.parent.item.id,
comp_id: arg.parent?.comp?.component?.id,
instance_id: arg.parent?.instance_id,
},
instances,
scope: {},
};
if (p.smeta?.[item.id]) {
meta.scope.def = p.smeta[item.id].scope;
}
if (item.id) {
if (p.set_meta !== false) {
p.meta[item.id] = meta;
}
}
walkProp({
item,
pcomp,
each(name, prop) {
const comp_id = item.component?.id;
if (
prop.meta?.type === "content-element" &&
prop.content &&
comp_id
) {
walkChild(prop.content, instance);
genMeta(p, {
item: prop.content,
is_root: false,
jsx_prop: {
is_root: true,
comp_id,
name,
},
parent: {
item,
instance_id: item.id,
comp: pcomp.comp,
},
});
}
},
});
if (p.on) {
if (p.on.item_exists && p.meta[item.id]) {
p.on.item_exists({ old: p.meta[item.id], new: meta });
} else if (p.on.item_new && !p.meta[item.id]) {
p.on.item_new({ new: meta });
}
}
if (p.on?.visit) {
p.on.visit(meta);
}
for (const child of Object.values(item.childs)) {
if (child.name.startsWith("jsx:")) continue;
genMeta(p, {
item: child,
is_root: false,
parent: {
item,
instance_id: item.id,
comp: pcomp.comp,
},
});
}
}
}
};
const getParentInstance = (p: GenMetaP, arg: GenMetaArg, id: string) => {
if (arg.parent?.instance_id && p.meta[arg.parent?.instance_id]) {
const parent_instance = p.meta[arg.parent?.instance_id];
if (parent_instance.instances) {
if (!parent_instance.instances[id]) {
parent_instance.instances[id] = {};
}
return parent_instance.instances[id];
}
}
};

View File

@ -1,30 +0,0 @@
import { IItem } from "../../../../../utils/types/item";
import { genMeta } from "../meta";
import { GenMetaP } from "../types";
export const initLoadComp = async (
p: GenMetaP,
item: IItem,
load: (comp_ids: string[]) => Promise<void>
) => {
const comp_ids: string[] = [];
genMeta(
{
...p,
on: {
visit_component: async (id) => {
if (!p.comps[id]) {
comp_ids.push(id);
}
},
},
set_meta: false,
},
{ item }
);
if (comp_ids.length > 0) {
await load(comp_ids);
await initLoadComp(p, item, load);
}
};

View File

@ -1,52 +0,0 @@
import { deepClone } from "web-utils";
import { IItem } from "../../../../../utils/types/item";
import { createId } from "@paralleldrive/cuid2";
import { FNComponent } from "../../../../../utils/types/meta-fn";
export const instantiate = (arg: {
item: IItem;
comp: IItem;
ids: Record<string, string>;
}) => {
const { item, comp, ids } = arg;
const newitem = deepClone(comp);
walkChild(newitem, ids);
if (item.id) {
newitem.id = item.id;
}
if (newitem.component && item.component && newitem.component.props) {
for (const k of Object.keys(newitem.component.props)) {
if (item.component.props[k]) {
newitem.component.props[k] = item.component.props[k];
}
}
}
for (const key of Object.keys(item)) {
delete (item as any)[key];
}
for (const [k, v] of Object.entries(newitem)) {
(item as any)[k] = v;
}
};
export const walkChild = (
item: IItem,
ids: Exclude<FNComponent["ref_ids"], undefined>
) => {
if (!ids[item.id]) {
ids[item.id] = createId();
}
item.id = ids[item.id];
if (item.childs) {
for (const child of item.childs) {
walkChild(child as IItem, ids);
}
}
};

View File

@ -1,23 +0,0 @@
import { IItem } from "../../../../../utils/types/item";
import { FNCompDef } from "../../../../../utils/types/meta-fn";
export const walkProp = (arg: {
item: IItem;
pcomp: { comp: IItem };
each: (name: string, prop: FNCompDef) => void;
}) => {
for (const [k, v] of Object.entries(arg.pcomp.comp.component?.props || {})) {
const props = arg.item.component?.props;
let prop = props?.[k];
if (props) {
if (!props[k]) {
props[k] = v;
prop = v;
}
}
if (prop) {
arg.each(k, prop);
}
}
};

View File

@ -1,62 +0,0 @@
import { IItem } from "../../../../utils/types/item";
import { genComp } from "./comp";
import { simplifyItemChild } from "./simplify";
import { GenMetaArg, GenMetaP, IMeta } from "./types";
export const genMeta = (p: GenMetaP, arg: GenMetaArg) => {
const item = arg.item as IItem;
if (item.type === "item" && item.component?.id) {
genComp(p, arg);
return;
}
let scope: IMeta["scope"] = {};
if (p.smeta) {
if (p.smeta[item.id] && p.smeta[item.id].scope) {
scope.def = p.smeta[item.id].scope;
}
}
const meta: IMeta = {
item: simplifyItemChild(item),
jsx_prop: arg.jsx_prop,
parent: {
id: arg.parent?.item.id || "root",
instance_id: arg.parent?.instance_id,
comp_id: arg.parent?.comp?.component?.id,
},
scope,
};
if (p.on?.visit) {
p.on.visit(meta);
}
if (p.on) {
if (p.on.item_exists && p.meta[item.id]) {
p.on.item_exists({ old: p.meta[item.id], new: meta });
} else if (p.on.item_new && !p.meta[item.id]) {
p.on.item_new({ new: meta });
}
}
if (item.id) {
if (p.set_meta !== false) {
p.meta[item.id] = meta;
}
}
if (item.childs) {
for (const [_, v] of Object.entries(item.childs)) {
genMeta(p, {
item: v,
is_root: false,
parent: {
item,
instance_id: arg.parent?.instance_id,
comp: arg.parent?.comp,
},
});
}
}
};

View File

@ -1,47 +0,0 @@
import { IItem } from "../../../../utils/types/item";
import { IMeta, ISimpleMeta } from "./types";
export const simplifyItemChild = (item: IItem) => {
const newitem = {} as any;
for (const [k, v] of Object.entries(item)) {
if (k === "childs") {
newitem.childs = [];
if (v && Array.isArray(v)) {
for (const child of v) {
newitem.childs.push({ id: child.id });
}
}
} else {
newitem[k] = v;
}
}
return newitem as IItem;
};
export const simplifyMeta = (allmeta: Record<string, IMeta>) => {
const smeta = {} as Record<string, ISimpleMeta>;
for (const [k, meta] of Object.entries(allmeta)) {
smeta[k] = {
id: meta.item.id,
parent: meta.parent
? {
id: meta.parent.id,
comp_id: meta.parent.comp_id,
instance_id: meta.parent.instance_id,
}
: undefined,
comp:
meta.item.component && meta.instances
? {
id: meta.item.component.id,
instances: meta.instances,
}
: undefined,
scope: meta.scope.def,
};
}
return smeta;
};

View File

@ -1,39 +0,0 @@
import { VG } from "./global";
export const preload = async (p: VG, pathname: string) => {
//TODO
};
export const extractNavigate = (str: string) => {
return [
...findBetween(str, `navigate(`, `)`),
...findBetween(str, `href = `, `;`),
];
};
const findBetween = (text: string, opener: string, closer: string) => {
let i = 0;
let last = 0;
const founds: string[] = [];
while (true) {
const startIndex = text.indexOf(opener, i);
last = i;
if (startIndex >= 0) {
const char = text[startIndex + opener.length];
if (char === '"' || char === "'" || char === "`") {
const end = text.indexOf(
`${char}${closer}`,
startIndex + opener.length + 1
);
const found = text.substring(startIndex + opener.length + 1, end);
i = end + 2 + closer.length;
founds.push(found);
}
}
if (last === i) {
break;
}
}
return founds;
};

View File

@ -1,15 +0,0 @@
import { IMeta, IScope, PG } from "../../ed/logic/ed-global";
export type VLoad =
| { mode: "page"; page_id: string }
| { mode: "pathname"; pathname: string }
| {
mode: "tree_meta";
entry: string[];
scope?: IScope;
meta: Record<string, IMeta>;
};
export type VLoadComponent = {
load: (id_comp: string) => Promise<void>;
};

View File

@ -1,15 +0,0 @@
import { useGlobal } from "web-utils";
import { ViewGlobal } from "../logic/global";
import { ViewMeta } from "./meta/meta";
export const VEntry = () => {
const v = useGlobal(ViewGlobal, "VIEW");
return (
<>
{v.entry.map((section_id) => {
return <ViewMeta id={section_id} key={section_id} />;
})}
</>
);
};

View File

@ -1,41 +0,0 @@
import { FC, Fragment, ReactNode } from "react";
import { useGlobal, useLocal } from "web-utils";
import { IMeta } from "../../../ed/logic/ed-global";
import { ViewGlobal } from "../../logic/global";
import { ViewMeta } from "./meta";
export const ViewMetaChildren: FC<{
meta: IMeta;
className?: string;
}> = ({ meta, className }) => {
const v = useGlobal(ViewGlobal, "VIEW");
const children: Record<string, ReactNode> = {};
const item = meta.item;
if (item.type !== "text") {
for (const child of item.childs) {
if (child.id) {
children[child.id] = <ViewMeta id={child.id} key={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] = (
<Fragment key={item.id}>
<ActiveText meta={meta} />
</Fragment>
);
} else {
children[item.id] = (
<span
key={item.id}
dangerouslySetInnerHTML={{ __html: item.html || "&nbsp;" }}
></span>
);
}
}
}
return <>{Object.values(children)}</>;
};

View File

@ -1,46 +0,0 @@
import { FC, useState } from "react";
import { useGlobal } from "web-utils";
import { ViewGlobal } from "../../logic/global";
import { ViewMetaRender } from "./render";
import { ViewBoundedScript } from "./script";
import { compPropVal } from "./script/comp-propval";
export const ViewMeta: FC<{ id: string; scopeIndex?: Record<string, any> }> = ({
id,
scopeIndex,
}) => {
const v = useGlobal(ViewGlobal, "VIEW");
const [, _render] = useState({});
const meta = v.meta[id];
if (!meta) return null;
meta.render = () => _render({});
const item = meta.item;
if (item.type === "item" && item.component?.id) {
compPropVal(v, meta, scopeIndex);
}
if (item.hidden && v.view.hidden) {
if (v.view.hidden(meta)) {
return null;
}
}
if (item.adv) {
if (item.adv.js && item.adv.jsBuilt && typeof item.adv.js === "string") {
return (
<ViewBoundedScript
js={item.adv.js}
v={v}
item={item}
scopeIndex={scopeIndex}
/>
);
}
}
return <ViewMetaRender meta={meta} v={v} />;
};

View File

@ -1,46 +0,0 @@
import { FC } from "react";
import { produceCSS } from "../../../../utils/css/gen";
import { IMeta } from "../../../ed/logic/ed-global";
import { VG } from "../../logic/global";
import { ViewMetaChildren } from "./children";
export const ViewMetaRender: FC<{
meta: IMeta;
v: VG;
props?: any;
className?: string;
}> = ({ meta, v, props, className }) => {
let _className = className;
const item = meta.item;
if (meta.is_layout && !v.layout.show) {
return <ViewMetaChildren key={item.id} meta={meta} />;
}
if (!className) {
_className = produceCSS(item, {
mode: v.mode,
hover: v.view.hover ? v.view.hover.get(meta) : undefined,
active: v.view.active ? v.view.active.get(meta) : undefined,
});
}
return (
<div
className={_className}
{...props}
onPointerOver={(e) => {
e.stopPropagation();
e.preventDefault();
v.view.hover?.set(meta);
}}
onPointerDown={(e) => {
e.stopPropagation();
e.preventDefault();
v.view.active?.set(meta);
}}
>
<ViewMetaChildren meta={meta} />
</div>
);
};

View File

@ -1,204 +0,0 @@
import hash_sum from "hash-sum";
import { FC, ReactNode, isValidElement, useEffect } from "react";
import { useLocal } from "web-utils";
import { produceCSS } from "../../../../utils/css/gen";
import { createAPI, createDB } from "../../../../utils/script/init-api";
import { IItem } from "../../../../utils/types/item";
import { ISection } from "../../../../utils/types/section";
import { IText } from "../../../../utils/types/text";
import { VG } from "../../logic/global";
import { extractNavigate, preload } from "../../logic/router";
import { ViewMetaChildren } from "./children";
import { createLocal } from "./script/create-local";
import { createPassProp } from "./script/create-pass-prop";
import { ErrorBox } from "./script/error-box";
import { mergeScopeUpwards } from "./script/merge-upward";
const renderLimitConfig = {
maxCount: 30,
maxTime: 10,
};
const renderLimit = {} as Record<
string,
Record<string, { ts: number; count: number; cache: ReactNode }>
>;
export const ViewBoundedScript: FC<{
v: VG;
item: IItem | IText | ISection;
scopeIndex?: Record<string, any>;
js: string;
}> = ({ item, v, scopeIndex, js }) => {
const local = useLocal({ js: "" });
useEffect(() => {
if (local.js !== js) {
local.js = js;
local.render();
}
}, [js]);
if (local.js !== js) {
return null;
}
return (
<ErrorBox silent={true}>
<ViewMetaScript v={v} item={item} scopeIndex={scopeIndex} />
</ErrorBox>
);
};
export const ViewMetaScript: FC<{
v: VG;
item: IItem | IText | ISection;
scopeIndex?: Record<string, any>;
}> = ({ item, v, scopeIndex }) => {
const js = item.adv?.jsBuilt;
const meta = v.meta[item.id];
const w = window as any;
const className = produceCSS(item, {
mode: v.mode,
hover: v.view.hover ? v.view.hover.get(meta) : undefined,
active: v.view.active ? v.view.active.get(meta) : undefined,
});
if (!renderLimit[v.current.page_id]) {
renderLimit[v.current.page_id] = {};
}
if (!renderLimit[v.current.page_id][item.id]) {
renderLimit[v.current.page_id][item.id] = {
ts: Date.now(),
count: 1,
cache: null,
};
}
if (
Math.abs(renderLimit[v.current.page_id][item.id].ts - Date.now()) <
renderLimitConfig.maxTime
) {
renderLimit[v.current.page_id][item.id].count++;
if (
renderLimit[v.current.page_id][item.id].count > renderLimitConfig.maxCount
) {
let js = "";
if (typeof item.adv?.js === "string") {
js = item.adv.js;
}
console.warn(
`Maximum render limit (${renderLimitConfig.maxCount} render in ${
renderLimitConfig.maxTime
}ms) reached in item [${item.name}]:\n${
js.length > 30 ? js.substring(0, 30) + "..." : js
}`
);
return renderLimit[v.current.page_id][item.id].cache;
}
} else {
renderLimit[v.current.page_id][item.id].ts = Date.now();
renderLimit[v.current.page_id][item.id].count = 1;
}
const children = <ViewMetaChildren key={item.id} meta={meta} />;
let args = {};
if (js && meta) {
if (!meta.memoize) {
meta.memoize = {};
}
const memoizeKey = hash_sum(scopeIndex) || "default";
if (!meta.memoize[memoizeKey]) {
meta.memoize[memoizeKey] = {
Local: createLocal(v, item.id, scopeIndex),
PassProp: createPassProp(v, item.id, scopeIndex),
};
}
const _js = item.adv?.js;
if (typeof _js === "string") {
const navs = extractNavigate(_js || "");
if (navs.length > 0) {
navs.map((nav) => preload(v, nav));
}
}
if (v.script.api_url) {
if (!v.script.db) v.script.db = createDB(v.script.api_url);
if (!v.script.api) v.script.api = createAPI(v.script.api_url);
}
const finalScope = mergeScopeUpwards(v, item.id, scopeIndex);
for (const [k, v] of Object.entries(finalScope)) {
if (v && typeof v === "object") {
const t: {
_jsx: true;
Comp: FC<{ parent_id: string; scopeIndex?: Record<string, any> }>;
} = v as any;
if (t._jsx && t.Comp) {
finalScope[k] = (
<>
<t.Comp parent_id={meta.item.id} scopeIndex={scopeIndex} />
</>
);
}
}
}
const output = { jsx: null as any };
args = {
...w.exports,
...finalScope,
...meta.memoize[memoizeKey],
db: v.script.db,
api: v.script.api,
children,
props: {
className,
onPointerOver: (e: any) => {
e.stopPropagation();
e.preventDefault();
v.view.hover?.set(meta);
},
onPointerDown: (e: any) => {
e.stopPropagation();
e.preventDefault();
v.view.active?.set(meta);
},
},
useEffect: useEffect,
render: (jsx: ReactNode) => {
if (isValidElement(jsx)) {
// output.jsx = (
// <>
// <div className={"absolute bg-white px-1 z-10 text-[9px] text-gray-500 -mt-1"}>{item.id}</div>
// {jsx}
// </>
// );
output.jsx = jsx;
renderLimit[v.current.page_id][item.id].cache = output.jsx;
}
},
};
// execute
const fn = new Function(...Object.keys(args), js);
const res = fn(...Object.values(args));
if (res instanceof Promise) {
res.catch((e: any) => {
console.warn(e);
console.warn(
(
`ERROR in ${item.type} [${item.name}]:\n ` +
((item.adv?.js || "") as any)
).trim()
);
console.warn(`Available var:`, args, `\n\n`);
});
}
return output.jsx;
}
};

View File

@ -1,130 +0,0 @@
import { createAPI, createDB } from "../../../../../utils/script/init-api";
import { IItem } from "../../../../../utils/types/item";
import { FNCompDef } from "../../../../../utils/types/meta-fn";
import { IMeta } from "../../../../ed/logic/ed-global";
import { VG } from "../../../logic/global";
import { extractNavigate, preload } from "../../../logic/router";
import { ViewMeta } from "../meta";
import { mergeScopeUpwards } from "./merge-upward";
const jsxProps = {} as Record<string, any>;
export const compPropVal = (
v: VG,
meta: IMeta,
scopeIndex?: Record<string, any>
) => {
let props = {} as Record<string, FNCompDef>;
let cprops = [] as [string, FNCompDef][];
const item = meta.item;
if (item.type === "item" && item.component?.id) {
const icomp = item.component;
if (icomp) {
props = icomp.props;
cprops = Object.entries({ ...icomp.props });
if (!v.script.db) v.script.db = createDB(v.script.api_url);
if (!v.script.api) v.script.api = createAPI(v.script.api_url);
const w = window as any;
const finalScope = mergeScopeUpwards(v, item.id, scopeIndex);
const args = {
...w.exports,
...finalScope,
db: v.script.db,
api: v.script.api,
};
const result: any = {};
for (const [name, _prop] of cprops) {
const prop = props[name] || _prop;
let value: any = null;
if (prop.valueBuilt) {
const fn = new Function(
...Object.keys(args),
`return ${prop.valueBuilt}`
);
try {
value = fn(...Object.values(args)) || null;
const navs = extractNavigate(prop.valueBuilt || "");
if (navs.length > 0) {
navs.map((nav) => preload(v, nav));
}
} catch (e) {
const cname = meta.item.name;
console.warn(e);
console.warn(
`ERROR in Component [${cname}], in prop [${name}]:\n ` +
prop.value
);
}
}
if (prop.meta?.type === "content-element") {
if (!(typeof value === "object" && !!value && value._jsx)) {
const id = `${meta.item.id}-${name}`;
if (!jsxProps[id]) {
jsxProps[id] = {
_jsx: true,
Comp: ({
parent_id,
scopeIndex,
}: {
parent_id: string;
scopeIndex?: Record<string, any>;
}) => {
if (prop.content) {
const meta = v.meta[prop.content.id] as IMeta;
let parent = v.meta[parent_id];
if (meta && parent) {
meta.parent_item.id = parent_id;
return (
<ViewMeta
id={prop.content.id}
scopeIndex={scopeIndex}
/>
);
}
}
return <></>;
},
};
}
value = jsxProps[id];
}
}
result[name] = value;
}
meta.propval = result;
const propvis: any = {};
for (const [name, _prop] of cprops) {
const prop = props[name] || _prop;
if (prop.visible) {
const finalArgs = { ...args, ...result };
try {
const fn = new Function(
...Object.keys(finalArgs),
`return ${_prop.visible}`
);
propvis[name] = fn(...Object.values(finalArgs));
} catch (e) {
const cname = meta.item.name;
console.warn(e);
console.warn(
`ERROR in Component [${cname}], in prop [${name}]:\n ` +
prop.visible
);
}
}
}
meta.propvis = propvis;
}
}
};

View File

@ -1,153 +0,0 @@
import { ReactNode, useEffect, useState } from "react";
import { deepClone } from "web-utils";
import { IMeta } from "../../../../ed/logic/ed-global";
import { VG } from "../../../logic/global";
import { modifyChildScopeIndex } from "./mod-scope-index";
const cachedLocal = {} as Record<string, Record<string, any>>;
const cachedPath = {} as Record<string, Record<string, any>>;
const cachedLayout = {} as Record<string, true>;
export const createLocal = (
v: VG,
id: string,
existingScopeIndex?: Record<string, any>
) => {
return ({
name,
idx: local_id,
value,
effect,
children,
hook,
deps,
cache,
}: {
name: string;
value: any;
idx?: string;
effect?: (value: any) => void | Promise<void>;
children: ReactNode;
hook?: (value: any) => void;
deps?: any[];
cache?: boolean;
}) => {
const meta = v.meta[id] as IMeta;
const [_, set] = useState({});
let scope = null as any;
if (!local_id) {
if (!meta.scope) {
meta.scope = {};
}
scope = meta.scope;
} else {
if (!meta.indexed_scope) {
meta.indexed_scope = {};
}
if (!meta.indexed_scope[local_id]) meta.indexed_scope[local_id] = {};
scope = meta.indexed_scope[local_id];
}
const render = () => {
if (!local_id) {
if (meta.render) meta.render();
else v.render();
} else {
set({});
}
};
const genScope = () => {
try {
const nval = deepClone(value);
if (!scope[name]) {
scope[name] = {
...nval,
render,
};
} else {
for (const [k, v] of Object.entries(nval)) {
scope[name][k] = v;
}
scope[name].render = render;
}
} catch (e) {
console.warn(e);
}
};
let page_id = v.current.page_id || "";
const itemid = meta.item.id + (local_id ? `_${local_id}` : "");
if (meta.is_layout) {
page_id = "layout";
if (cachedLayout[meta.item.id]) {
scope[name] = cachedLocal[page_id][itemid];
scope[name].render = render;
}
}
if (
page_id !== "layout" ||
(page_id === "layout" && !cachedLayout[meta.item.id])
) {
if (!cachedLocal[page_id]) {
cachedLocal[page_id] = {};
}
if (!cachedPath[page_id]) {
cachedPath[page_id] = {};
}
if (cachedLocal[page_id][itemid]) {
if (cache === false) {
if (cachedPath[page_id][itemid] !== location.href) {
cachedPath[page_id][itemid] = location.href;
genScope();
cachedLocal[page_id][itemid] = scope[name];
} else {
scope[name] = cachedLocal[page_id][itemid];
scope[name].render = render;
}
} else {
scope[name] = cachedLocal[page_id][itemid];
scope[name].render = render;
}
} else {
genScope();
cachedLocal[page_id][itemid] = scope[name];
cachedPath[page_id][itemid] = location.href;
}
}
if (typeof hook === "function") {
try {
hook(scope[name]);
} catch (e) {
console.warn(e);
}
}
useEffect(() => {
if (effect) {
if (meta.is_layout) {
if (cachedLayout[meta.item.id]) {
return;
}
cachedLayout[meta.item.id] = true;
}
try {
effect(scope[name]);
} catch (e) {
console.warn(e);
}
}
}, [...(deps || []), location.href]);
if (local_id) {
const scopeIndex = { ...existingScopeIndex, [meta.item.id]: local_id };
return modifyChildScopeIndex(children, scopeIndex);
}
return children;
};
};

View File

@ -1,56 +0,0 @@
import { ReactNode } from "react";
import { VG } from "../../../logic/global";
import { modifyChildScopeIndex } from "./mod-scope-index";
import hash_sum from "hash-sum";
export const createPassProp = (
v: VG,
id: string,
existingScopeIndex?: Record<string, any>
) => {
return (arg: Record<string, any> & { children: ReactNode; idx?: any }) => {
const meta = v.meta[id];
if (arg.idx &&meta && meta.item && meta.item.id) {
let idx = arg.idx;
if (!idx) {
const narg: any = {};
for (const [k, v] of Object.entries(arg)) {
if (typeof v !== "object" && typeof v !== "function") {
narg[k] = v;
}
}
idx = hash_sum(narg);
arg.idx = idx;
}
meta.indexed_scope[idx] = {};
for (const [k, v] of Object.entries(arg)) {
if (k === "children") continue;
meta.indexed_scope[idx][k] = v;
}
const scopeIndex = { ...existingScopeIndex, [meta.item.id]: idx };
if (!meta.scope) {
meta.scope = {};
}
for (const [k, v] of Object.entries(arg)) {
if (k === "children") continue;
meta.scope[k] = v;
}
return modifyChildScopeIndex(arg.children, scopeIndex);
}
if (!meta.scope) {
meta.scope = {};
}
for (const [k, v] of Object.entries(arg)) {
if (k === "children") continue;
meta.scope[k] = v;
}
return arg.children;
};
};

View File

@ -1,61 +0,0 @@
import { useErrorBoundary, withErrorBoundary } from "react-use-error-boundary";
import { useGlobal, useLocal } from "web-utils";
import { IMeta } from "../../../../ed/logic/ed-global";
import { ViewGlobal } from "../../../logic/global";
export const ErrorBox = withErrorBoundary(
({
children,
meta,
id,
silent,
}: {
children: any;
meta?: IMeta;
id?: string;
silent?: boolean;
}) => {
const local = useLocal({ retrying: false });
const [error, resetError] = useErrorBoundary((error, errorInfo) => {
if (silent !== true) console.warn(error);
});
let _meta = meta;
if (id) {
const p = useGlobal(ViewGlobal, "VIEW");
_meta = p.meta[id];
}
if (error) {
return (
<div className="bg-red-100 border border-red-300 rounded-sm text-xs flex flex-col items-center">
<div className="text-[10px] font-bold text-red-900 self-stretch px-1">
ERROR {_meta?.item.name ? "[" + _meta.item.name + "]:" : ""}
</div>
<p className="border-b border-red-300 px-1 pb-1 min-w-[100px]">
{!local.retrying ? <>{(error as any).message}</> : <>Retrying...</>}
</p>
<div className="p-1">
<button
onClick={() => {
local.retrying = true;
local.render();
setTimeout(() => {
local.retrying = false;
local.render();
resetError();
}, 100);
}}
className="bg-white border border-white hover:border-red-400 hover:bg-red-50 rounded px-2"
>
Try again
</button>
</div>
</div>
);
}
return children;
}
);

View File

@ -1,49 +0,0 @@
import { IMeta } from "../../../../ed/logic/ed-global";
import { VG } from "../../../logic/global";
export const mergeScopeUpwards = (
v: VG,
id: string,
scopeIndex?: Record<string, any>,
each?: (meta: IMeta, values: Record<string, any>) => boolean
) => {
const meta = v.meta[id];
if (!meta.scope) {
meta.scope = {};
}
let cur = meta;
const finalScope: any = {};
while (cur) {
let scope = null;
let indexedScope = null;
if (cur.indexed_scope && scopeIndex) {
const idx = scopeIndex[cur.item.id];
if (typeof idx !== "undefined" && cur.indexed_scope[idx]) {
indexedScope = cur.indexed_scope[idx];
}
}
if (indexedScope || cur.scope || cur.propval) {
scope = { ...cur.scope, ...indexedScope, ...cur.propval };
for (const [k, v] of Object.entries(scope)) {
if (typeof finalScope[k] === "undefined") finalScope[k] = v;
}
if (each) {
if (!each(cur, scope)) {
break;
}
}
}
cur = v.meta[cur.parent_item.id];
}
return finalScope;
};

View File

@ -1,27 +0,0 @@
import { Fragment, ReactNode } from "react";
export const modifyChildScopeIndex = (
child: ReactNode | ReactNode[],
scopeIndex: Record<string, any>
) => {
if (Array.isArray(child)) {
const childs: any[] = [];
for (const c of child) {
childs.push(modifyChildScopeIndex(c, scopeIndex));
}
return childs;
}
if (typeof child === "object" && child) {
const c = child as any;
if (c.type === Fragment) {
return (
<Fragment key={c.key}>
{modifyChildScopeIndex(c.props.children, scopeIndex)}
</Fragment>
);
}
return { ...child, props: { ...(child as any).props, scopeIndex } };
}
return child;
};

View File

@ -1,113 +0,0 @@
import { FC, ReactNode, Suspense } from "react";
import { useGlobal } from "web-utils";
import { Loading } from "../../utils/ui/loading";
import { IMeta } from "../ed/logic/ed-global";
import { ViewGlobal } from "./logic/global";
import { vInit } from "./logic/init";
import { newLoadCode } from "./logic/load-code-new";
import { oldLoadCode } from "./logic/load-code-old";
import { VLoad, VLoadComponent } from "./logic/types";
import { VEntry } from "./render/entry";
import { ErrorBox } from "./render/meta/script/error-box";
type ViewProp = {
load: VLoad;
component: VLoadComponent;
site_id: string;
page_id: string;
api_url: string;
mode: "desktop" | "mobile";
code_mode?: "old" | "new";
layout?: { show: boolean };
isEditor?: boolean;
bind?: (arg: { render: () => void }) => void;
hidden?: (item: IMeta) => boolean;
hover?: { get: (item: IMeta) => boolean; set: (meta: IMeta) => void };
active?: {
get: (item: IMeta) => boolean;
set: (item: IMeta) => void;
text?: (arg: { meta: IMeta }) => ReactNode;
};
};
export const View: FC<ViewProp> = (props) => {
return (
<ErrorBox>
<Suspense>
<BoxedView {...props} />
</Suspense>
</ErrorBox>
);
};
const BoxedView: FC<ViewProp> = ({
load,
site_id,
layout,
page_id,
bind,
code_mode,
hover,
active,
hidden,
component,
api_url,
mode,
isEditor,
}) => {
const v = useGlobal(ViewGlobal, "VIEW");
v.script.api_url = api_url;
if (hidden) v.view.hidden = hidden;
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";
}
v.layout = layout ? layout : { show: false };
v.component.load = component.load;
if (bind) {
bind({
render() {
v.status = "rebuild";
v.render();
},
});
}
if (v.status === "init") {
vInit(v, { load, page_id, site_id, mode, isEditor: !!isEditor });
if (v.status === "init") {
return <Loading backdrop={false} note="init" />;
}
}
if (v.status === "load-code" || v.status === "loading-code") {
if (!code_mode || code_mode === "new") {
newLoadCode(v);
} else {
oldLoadCode(v);
}
if (v.status === "load-code" || v.status === "loading-code") {
return (
<>
<Loading backdrop={false} note="rendering-view" />
</>
);
}
}
if (v.status === "rebuild") {
if (load.mode === "tree_meta") {
v.meta = load.meta;
v.entry = load.entry;
}
v.body_cache = <VEntry />;
v.status = "ready";
}
return <div className="flex flex-1 flex-col relative">{v.body_cache}</div>;
};