check point
This commit is contained in:
parent
9755f4bc1e
commit
3f780647b2
File diff suppressed because one or more lines are too long
|
|
@ -1,18 +1,72 @@
|
||||||
|
import globalExternals from "@fal-works/esbuild-plugin-global-externals";
|
||||||
|
import style from "@hyrious/esbuild-plugin-style";
|
||||||
import { dir } from "dir";
|
import { dir } from "dir";
|
||||||
import { code } from "../../code";
|
import { context } from "esbuild";
|
||||||
import { Parcel } from "@parcel/core";
|
|
||||||
import { removeAsync } from "fs-jetpack";
|
import { removeAsync } from "fs-jetpack";
|
||||||
import { $ } from "bun";
|
import { code } from "../../code";
|
||||||
let bundler = new Parcel({
|
|
||||||
entries: "a.js",
|
|
||||||
defaultConfig: "@parcel/config-default",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const initFrontEnd = async (root: string, id_site: string) => {
|
export const initFrontEnd = async (root: string, id_site: string) => {
|
||||||
let existing = code.internal.frontend[id_site];
|
let existing = code.internal.frontend[id_site];
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
// const run = $``
|
const build_path = dir.data(`code/${id_site}/site/build`);
|
||||||
|
await removeAsync(build_path);
|
||||||
|
existing = await context({
|
||||||
|
absWorkingDir: root,
|
||||||
|
entryPoints: ["index.tsx"],
|
||||||
|
outdir: build_path,
|
||||||
|
format: "esm",
|
||||||
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
treeShaking: true,
|
||||||
|
splitting: true,
|
||||||
|
logLevel: "silent",
|
||||||
|
sourcemap: true,
|
||||||
|
plugins: [
|
||||||
|
style(),
|
||||||
|
globalExternals({
|
||||||
|
react: {
|
||||||
|
varName: "window.React",
|
||||||
|
type: "cjs",
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
varName: "window.ReactDOM",
|
||||||
|
type: "cjs",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "prasi",
|
||||||
|
setup(setup) {
|
||||||
|
setup.onEnd(async (res) => {
|
||||||
|
if (res.errors.length > 0) {
|
||||||
|
await codeError(
|
||||||
|
id_site,
|
||||||
|
res.errors.map((e) => e.text).join("\n\n"),
|
||||||
|
"site"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
code.internal.frontend[id_site] = existing;
|
||||||
|
existing.watch()
|
||||||
}
|
}
|
||||||
|
await code.internal.frontend[id_site].rebuild()
|
||||||
|
};
|
||||||
|
|
||||||
|
const codeError = async (
|
||||||
|
id_site: string,
|
||||||
|
error: string,
|
||||||
|
mode: "server" | "site"
|
||||||
|
) => {
|
||||||
|
const path = code.path(
|
||||||
|
id_site,
|
||||||
|
"site",
|
||||||
|
"src",
|
||||||
|
mode === "server" ? "server.log" : "index.log"
|
||||||
|
);
|
||||||
|
|
||||||
|
await Bun.write(path, error);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export const codeInternal = {
|
||||||
get frontend() {
|
get frontend() {
|
||||||
if (!g.prasi_code) g.prasi_code = {};
|
if (!g.prasi_code) g.prasi_code = {};
|
||||||
if (!g.prasi_code.frontend) g.prasi_code.frontend = {};
|
if (!g.prasi_code.frontend) g.prasi_code.frontend = {};
|
||||||
return g.prasi_code.frontend as Record<SITE_ID, Parcel>;
|
return g.prasi_code.frontend as Record<SITE_ID, BuildContext>;
|
||||||
},
|
},
|
||||||
get typings() {
|
get typings() {
|
||||||
if (!g.prasi_code) g.prasi_code = {};
|
if (!g.prasi_code) g.prasi_code = {};
|
||||||
|
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
import { FC, useEffect } from "react";
|
|
||||||
import { page, useGlobal, useLocal } from "web-utils";
|
|
||||||
import { EditorGlobal } from "../../render/editor/logic/global";
|
|
||||||
import { Loading } from "../../utils/ui/loading";
|
|
||||||
|
|
||||||
export default page({
|
|
||||||
url: "/editor/:site_id/:page_id",
|
|
||||||
component: ({}) => {
|
|
||||||
const p = useGlobal(EditorGlobal, "EDITOR");
|
|
||||||
|
|
||||||
const local = useLocal({
|
|
||||||
loading: true,
|
|
||||||
session: null as any,
|
|
||||||
notfound: false,
|
|
||||||
init: false,
|
|
||||||
Editor: null as null | FC<any>,
|
|
||||||
});
|
|
||||||
const site_id = params.site_id === "_" ? "" : params.site_id;
|
|
||||||
const page_id = params.page_id === "_" ? "" : params.page_id;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!local.init) {
|
|
||||||
(async () => {
|
|
||||||
if (!local.Editor) {
|
|
||||||
local.Editor = (await import("../../render/editor/editor")).Editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ses: any = null;
|
|
||||||
try {
|
|
||||||
ses = JSON.parse(localStorage.getItem("prasi-session") || "");
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
await new Promise<void>(async (done) => {
|
|
||||||
try {
|
|
||||||
if (!!ses) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
let e = await _api.session();
|
|
||||||
if (!e) {
|
|
||||||
(window as any).redirectTo = location.pathname;
|
|
||||||
navigate("/login");
|
|
||||||
localStorage.removeItem("prasi-session");
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("prasi-session", JSON.stringify(e));
|
|
||||||
}
|
|
||||||
if (!ses) {
|
|
||||||
ses = e;
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ses) {
|
|
||||||
local.session = ses;
|
|
||||||
|
|
||||||
if (!site_id) {
|
|
||||||
const res = await _db.site.findFirst({
|
|
||||||
where: {
|
|
||||||
is_deleted: false,
|
|
||||||
org: {
|
|
||||||
org_user: {
|
|
||||||
some: { id_user: ses.data.user.id },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (res) {
|
|
||||||
const page = await _db.page.findFirst({
|
|
||||||
where: {
|
|
||||||
id_site: res.id,
|
|
||||||
is_deleted: false,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (page) {
|
|
||||||
local.loading = false;
|
|
||||||
local.render();
|
|
||||||
navigate(`/editor/${res.id}/${page.id}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
local.loading = false;
|
|
||||||
local.render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (!page_id) {
|
|
||||||
let res = await _db.page.findFirst({
|
|
||||||
where: {
|
|
||||||
id_site: site_id,
|
|
||||||
is_deleted: false,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res) {
|
|
||||||
res = await _db.page.create({
|
|
||||||
data: {
|
|
||||||
content_tree: {
|
|
||||||
childs: [],
|
|
||||||
id: "root",
|
|
||||||
type: "root",
|
|
||||||
},
|
|
||||||
name: "home",
|
|
||||||
url: "/",
|
|
||||||
id_site: site_id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
local.loading = false;
|
|
||||||
local.render();
|
|
||||||
navigate(`/editor/${site_id}/${res.id}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local.init = true;
|
|
||||||
local.loading = false;
|
|
||||||
local.render();
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}, [local.init]);
|
|
||||||
|
|
||||||
const Editor = local.Editor;
|
|
||||||
if (local.loading || !Editor) return <Loading note="base-page" />;
|
|
||||||
|
|
||||||
const sw = navigator.serviceWorker.controller;
|
|
||||||
if (sw) {
|
|
||||||
sw.postMessage({
|
|
||||||
type: "add-cache",
|
|
||||||
url: location.href,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Editor session={local.session} site_id={site_id} page_id={page_id} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { validate } from "uuid";
|
|
||||||
import { page } from "web-utils";
|
|
||||||
import { devLoader } from "../../render/live/dev-loader";
|
|
||||||
import { Live } from "../../render/live/live";
|
|
||||||
|
|
||||||
export default page({
|
|
||||||
url: "/live/:domain/**",
|
|
||||||
component: ({}) => {
|
|
||||||
params.site_id = params.domain;
|
|
||||||
let pathname = `/${params._ === "_" ? "" : params._}`;
|
|
||||||
if (validate(params._)) {
|
|
||||||
const arr = params._.split("/");
|
|
||||||
params.page_id = arr.shift();
|
|
||||||
pathname = `/${arr.join("/")}`;
|
|
||||||
}
|
|
||||||
(window as any).pathname = pathname;
|
|
||||||
|
|
||||||
if (navigator.serviceWorker) {
|
|
||||||
navigator.serviceWorker.controller?.postMessage({
|
|
||||||
type: "add-cache",
|
|
||||||
url: location.href,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Live
|
|
||||||
domain_or_siteid={params.domain}
|
|
||||||
pathname={pathname}
|
|
||||||
loader={devLoader}
|
|
||||||
liveSync
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -18,14 +18,6 @@ export const ed = {
|
||||||
url: "/ed/:site_id/:page_id",
|
url: "/ed/:site_id/:page_id",
|
||||||
page: () => import("./page/ed"),
|
page: () => import("./page/ed"),
|
||||||
};
|
};
|
||||||
export const editor = {
|
|
||||||
url: "/editor/:site_id/:page_id",
|
|
||||||
page: () => import("./page/editor"),
|
|
||||||
};
|
|
||||||
export const live = {
|
|
||||||
url: "/live/:domain/**",
|
|
||||||
page: () => import("./page/live"),
|
|
||||||
};
|
|
||||||
export const vi = {
|
export const vi = {
|
||||||
url: "/vi/:domain/**",
|
url: "/vi/:domain/**",
|
||||||
page: () => import("./page/vi"),
|
page: () => import("./page/vi"),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { loadApiProxyDef } from "./base/load/api/api-proxy-def";
|
||||||
import { dbProxy } from "./base/load/db/db-proxy";
|
import { dbProxy } from "./base/load/db/db-proxy";
|
||||||
import { Root } from "./base/root";
|
import { Root } from "./base/root";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { registerMobile } from "./render/live/logic/mobile";
|
|
||||||
import { sworkerAddCache, sworkerRegister } from "./sworker-boot";
|
import { sworkerAddCache, sworkerRegister } from "./sworker-boot";
|
||||||
import { w } from "./utils/types/general";
|
import { w } from "./utils/types/general";
|
||||||
|
|
||||||
|
|
@ -13,7 +12,6 @@ const start = async () => {
|
||||||
let react = {
|
let react = {
|
||||||
root: null as null | ReactRoot,
|
root: null as null | ReactRoot,
|
||||||
};
|
};
|
||||||
w.mobile = registerMobile();
|
|
||||||
|
|
||||||
const cur = new URL(w.basehost || location.href);
|
const cur = new URL(w.basehost || location.href);
|
||||||
const base_url = `${cur.protocol}//${cur.host}`;
|
const base_url = `${cur.protocol}//${cur.host}`;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { syncronize } from "y-pojo";
|
import { syncronize } from "y-pojo";
|
||||||
import { fillID } from "../../../../../../../render/editor/tools/fill-id";
|
|
||||||
import { IContent, MContent } from "../../../../../../../utils/types/general";
|
import { IContent, MContent } from "../../../../../../../utils/types/general";
|
||||||
import { IItem } from "../../../../../../../utils/types/item";
|
import { IItem } from "../../../../../../../utils/types/item";
|
||||||
import { getMetaById } from "../../../../../logic/active/get-meta";
|
import { getMetaById } from "../../../../../logic/active/get-meta";
|
||||||
import { PG } from "../../../../../logic/ed-global";
|
import { PG } from "../../../../../logic/ed-global";
|
||||||
import { treeRebuild } from "../../../../../logic/tree/build";
|
import { treeRebuild } from "../../../../../logic/tree/build";
|
||||||
|
import { fillID } from "../../../../../logic/tree/fill-id";
|
||||||
|
|
||||||
export const edActionClone = (p: PG, item: IContent) => {
|
export const edActionClone = (p: PG, item: IContent) => {
|
||||||
const mitem = getMetaById(p, item.id)?.mitem;
|
const mitem = getMetaById(p, item.id)?.mitem;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { syncronize } from "y-pojo";
|
import { syncronize } from "y-pojo";
|
||||||
import { fillID } from "../../../../../../../render/editor/tools/fill-id";
|
|
||||||
import { IItem } from "../../../../../../../utils/types/item";
|
import { IItem } from "../../../../../../../utils/types/item";
|
||||||
import { getMetaById } from "../../../../../logic/active/get-meta";
|
import { getMetaById } from "../../../../../logic/active/get-meta";
|
||||||
import { PG } from "../../../../../logic/ed-global";
|
import { PG } from "../../../../../logic/ed-global";
|
||||||
import { treeRebuild } from "../../../../../logic/tree/build";
|
import { treeRebuild } from "../../../../../logic/tree/build";
|
||||||
|
import { fillID } from "../../../../../logic/tree/fill-id";
|
||||||
|
|
||||||
export const edActionDetach = (p: PG, item: IItem) => {
|
export const edActionDetach = (p: PG, item: IItem) => {
|
||||||
const mitem = getMetaById(p, item.id)?.mitem;
|
const mitem = getMetaById(p, item.id)?.mitem;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { syncronize } from "y-pojo";
|
import { syncronize } from "y-pojo";
|
||||||
import { fillID } from "../../../../../../../render/editor/tools/fill-id";
|
|
||||||
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 { MSection } from "../../../../../../../utils/types/section";
|
import { MSection } from "../../../../../../../utils/types/section";
|
||||||
|
|
@ -8,6 +7,7 @@ import { PG, active } from "../../../../../logic/ed-global";
|
||||||
import { treeRebuild } from "../../../../../logic/tree/build";
|
import { treeRebuild } from "../../../../../logic/tree/build";
|
||||||
import { TypedArray } from "yjs-types";
|
import { TypedArray } from "yjs-types";
|
||||||
import { loadComponent } from "../../../../../logic/comp/load";
|
import { loadComponent } from "../../../../../logic/comp/load";
|
||||||
|
import { fillID } from "../../../../../logic/tree/fill-id";
|
||||||
|
|
||||||
export const edActionPaste = async (p: PG, item: IContent) => {
|
export const edActionPaste = async (p: PG, item: IContent) => {
|
||||||
let mitem = getMetaById(p, item.id)?.mitem;
|
let mitem = getMetaById(p, item.id)?.mitem;
|
||||||
|
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
import { LSite, Loader } from "./logic/global";
|
|
||||||
|
|
||||||
const cache = { site: null as any, pages: [] as any, api: null };
|
|
||||||
const config = { serverurl: "" };
|
|
||||||
|
|
||||||
export const devLoader: Loader = {
|
|
||||||
async site(p, where) {
|
|
||||||
config.serverurl = serverurl;
|
|
||||||
const site = (await _db.site.findFirst({
|
|
||||||
where:
|
|
||||||
where.type === "siteid" ? { id: where.id } : { domain: where.domain },
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
config: true,
|
|
||||||
domain: true,
|
|
||||||
name: true,
|
|
||||||
js: true,
|
|
||||||
responsive: true,
|
|
||||||
js_compiled: true,
|
|
||||||
},
|
|
||||||
})) as unknown as LSite;
|
|
||||||
|
|
||||||
if (!site) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cgroups = await _db.site_use_comp.findMany({
|
|
||||||
where: { id_site: site.id },
|
|
||||||
select: { use_id_site: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Array.isArray(cgroups)) {
|
|
||||||
site.cgroup_ids = [];
|
|
||||||
for (const id of cgroups.map((c: any) => c.use_id_site)) {
|
|
||||||
site.cgroup_ids.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const layout = await _db.page.findFirst({
|
|
||||||
where: {
|
|
||||||
id_site: site.id,
|
|
||||||
name: { startsWith: "layout:" },
|
|
||||||
is_default_layout: true,
|
|
||||||
is_deleted: false,
|
|
||||||
},
|
|
||||||
select: { content_tree: true, id: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (layout && layout.content_tree) {
|
|
||||||
const childs = (layout.content_tree as any).childs;
|
|
||||||
if (childs && childs.length > 0) {
|
|
||||||
site.layout = childs[0];
|
|
||||||
site.layout_id = layout.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.site = site;
|
|
||||||
|
|
||||||
return site;
|
|
||||||
},
|
|
||||||
async comp(p, id) {
|
|
||||||
const comp = await load(`${config.serverurl}/_web/comp/${id}`);
|
|
||||||
p.comps.all[id] = comp;
|
|
||||||
return comp;
|
|
||||||
},
|
|
||||||
npm(p, type, id) {
|
|
||||||
if (type === "site") {
|
|
||||||
return `${config.serverurl}/npm/site/${id}/site.js`;
|
|
||||||
}
|
|
||||||
return `${config.serverurl}/npm/page/${id}/page.js`;
|
|
||||||
},
|
|
||||||
async page(p, id) {
|
|
||||||
return await load(`${config.serverurl}/_web/page/${id}`);
|
|
||||||
},
|
|
||||||
async pages(p, id) {
|
|
||||||
let pages = [] as any;
|
|
||||||
/** load pages */
|
|
||||||
const pagesLocal = localStorage.getItem(`prasi-pages-[${id}]`);
|
|
||||||
if (pagesLocal) {
|
|
||||||
try {
|
|
||||||
pages = JSON.parse(pagesLocal);
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadPages = async () => {
|
|
||||||
return await _db.page.findMany({
|
|
||||||
where: {
|
|
||||||
id_site: cache.site.id,
|
|
||||||
is_deleted: false,
|
|
||||||
name: { not: { startsWith: "layout:" } },
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
url: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pages.length === 0) {
|
|
||||||
pages = await loadPages();
|
|
||||||
localStorage.setItem(`prasi-pages-[${id}]`, JSON.stringify(pages));
|
|
||||||
} else {
|
|
||||||
loadPages().then((pages) => {
|
|
||||||
localStorage.setItem(`prasi-pages-[${id}]`, JSON.stringify(pages));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return pages;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const load = async (url: string) => {
|
|
||||||
const res = await fetch(url);
|
|
||||||
try {
|
|
||||||
const text = await res.text();
|
|
||||||
const json = JSON.parse(text);
|
|
||||||
return json;
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import { FC } from "react";
|
|
||||||
import { LRender } from "./l-render";
|
|
||||||
import { LText } from "./l-text";
|
|
||||||
|
|
||||||
export const LItem: FC<{
|
|
||||||
id: string;
|
|
||||||
_scopeIndex?: Record<string, any>;
|
|
||||||
fromProp?: boolean;
|
|
||||||
}> = ({ id, fromProp, _scopeIndex }) => {
|
|
||||||
return (
|
|
||||||
<LRender id={id} _scopeIndex={_scopeIndex}>
|
|
||||||
{(childs) => {
|
|
||||||
return childs.map((e) => {
|
|
||||||
if (e.type === "item") {
|
|
||||||
return (
|
|
||||||
<LItem
|
|
||||||
id={e.id}
|
|
||||||
key={e.id}
|
|
||||||
fromProp={fromProp}
|
|
||||||
_scopeIndex={_scopeIndex}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<LText
|
|
||||||
id={e.id}
|
|
||||||
key={e.id}
|
|
||||||
fromProp={fromProp}
|
|
||||||
_scopeIndex={_scopeIndex}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
</LRender>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import { useGlobal } from "web-utils";
|
|
||||||
import { LiveGlobal } from "../logic/global";
|
|
||||||
import { LSection } from "./l-section";
|
|
||||||
import { Loading } from "../../../utils/ui/loading";
|
|
||||||
|
|
||||||
export const LPage = () => {
|
|
||||||
const p = useGlobal(LiveGlobal, "LIVE");
|
|
||||||
const mode = p.mode;
|
|
||||||
|
|
||||||
let childs = Object.values(p.page?.content_tree?.childs || []);
|
|
||||||
if (p.layout.section && p.layout.content) {
|
|
||||||
childs = [p.layout.section];
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootChilds: string[] | undefined = Object.values(childs).map(
|
|
||||||
(e) => e.id
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={cx("relative flex flex-1 items-center justify-center")}>
|
|
||||||
<div
|
|
||||||
className={cx(
|
|
||||||
"absolute flex flex-col items-stretch flex-1 bg-white ",
|
|
||||||
mode === "mobile"
|
|
||||||
? css`
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
border-right: 1px solid #ccc;
|
|
||||||
width: 375px;
|
|
||||||
top: 0px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
bottom: 0px;
|
|
||||||
}
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
top: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
: "inset-0 overflow-auto",
|
|
||||||
|
|
||||||
css`
|
|
||||||
contain: content;
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{p.status === "ready" || p.status === "tree-rebuild" ? (
|
|
||||||
rootChilds?.map((id) => <LSection key={id} id={id} />)
|
|
||||||
) : (
|
|
||||||
<Loading />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mobileCSS = css`
|
|
||||||
background-color: white;
|
|
||||||
background-image: linear-gradient(45deg, #fafafa 25%, transparent 25%),
|
|
||||||
linear-gradient(-45deg, #fafafa 25%, transparent 25%),
|
|
||||||
linear-gradient(45deg, transparent 75%, #fafafa 75%),
|
|
||||||
linear-gradient(-45deg, transparent 75%, #fafafa 75%);
|
|
||||||
|
|
||||||
background-size: 20px 20px;
|
|
||||||
background-position:
|
|
||||||
0 0,
|
|
||||||
0 10px,
|
|
||||||
10px -10px,
|
|
||||||
-10px 0px;
|
|
||||||
`;
|
|
||||||
|
|
@ -1,243 +0,0 @@
|
||||||
import { FC, ReactNode, useEffect, useState } from "react";
|
|
||||||
import { deepClone, useGlobal, useLocal } from "web-utils";
|
|
||||||
import { produceCSS } from "../../../utils/css/gen";
|
|
||||||
import { IContent } from "../../../utils/types/general";
|
|
||||||
import { FNAdv, FNCompDef, FNLinkTag } from "../../../utils/types/meta-fn";
|
|
||||||
import { responsiveVal } from "../../editor/tools/responsive-val";
|
|
||||||
import { ItemMeta, LiveGlobal, PG } from "../logic/global";
|
|
||||||
import { preload } from "../logic/route";
|
|
||||||
import { treePropEval } from "../logic/tree-prop";
|
|
||||||
import { treeScopeEval } from "../logic/tree-scope";
|
|
||||||
import { LTextInternal } from "./l-text";
|
|
||||||
|
|
||||||
export const LRender: FC<{
|
|
||||||
id: string;
|
|
||||||
children?: (childs: IContent[]) => ReactNode;
|
|
||||||
fromProp?: boolean;
|
|
||||||
_scopeIndex?: Record<string, any>;
|
|
||||||
}> = ({ id, children, fromProp, _scopeIndex }) => {
|
|
||||||
const p = useGlobal(LiveGlobal, "LIVE");
|
|
||||||
const meta = p.treeMeta[id];
|
|
||||||
|
|
||||||
if (meta) {
|
|
||||||
if (meta.item.name.startsWith("::")) {
|
|
||||||
if (meta.isLayout) {
|
|
||||||
meta.className = produceCSS(meta.item, {
|
|
||||||
mode: p.mode,
|
|
||||||
});
|
|
||||||
|
|
||||||
return <PrasiPortal name={meta.item.name} />;
|
|
||||||
} else {
|
|
||||||
if (!p.portal[meta.item.name]) return null;
|
|
||||||
p.portal[meta.item.name].el = (
|
|
||||||
<LRenderInternal
|
|
||||||
p={p}
|
|
||||||
id={id}
|
|
||||||
children={children}
|
|
||||||
fromProp={fromProp}
|
|
||||||
meta={meta}
|
|
||||||
_scopeIndex={_scopeIndex}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LRenderInternal
|
|
||||||
p={p}
|
|
||||||
id={id}
|
|
||||||
children={children}
|
|
||||||
fromProp={fromProp}
|
|
||||||
meta={meta}
|
|
||||||
_scopeIndex={_scopeIndex}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export const LRenderInternal: FC<{
|
|
||||||
id: string;
|
|
||||||
children?: (childs: IContent[]) => ReactNode;
|
|
||||||
fromProp?: boolean;
|
|
||||||
meta?: ItemMeta;
|
|
||||||
_scopeIndex?: Record<string, any>;
|
|
||||||
p: PG;
|
|
||||||
}> = ({ id, children, meta, p, _scopeIndex }) => {
|
|
||||||
const [_, render] = useState({});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (meta) {
|
|
||||||
meta.mounted = true;
|
|
||||||
if (meta.pendingRender) {
|
|
||||||
meta.pendingRender = false;
|
|
||||||
render({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!meta) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
meta.render = () => {
|
|
||||||
if (meta) {
|
|
||||||
if (meta.mounted) {
|
|
||||||
render({});
|
|
||||||
} else {
|
|
||||||
meta.pendingRender = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let item = meta.item;
|
|
||||||
|
|
||||||
if (meta.comp?.id) {
|
|
||||||
const comp = meta.comp;
|
|
||||||
|
|
||||||
let props = {} as Record<string, FNCompDef>;
|
|
||||||
let cprops = {} as [string, FNCompDef][];
|
|
||||||
|
|
||||||
props = deepClone(
|
|
||||||
p.comps.all[meta.comp.id]?.content_tree.component?.props || {}
|
|
||||||
);
|
|
||||||
cprops = Object.entries(props);
|
|
||||||
|
|
||||||
comp.propval = treePropEval(p, id, cprops, _scopeIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _children = null;
|
|
||||||
|
|
||||||
if (children) {
|
|
||||||
if (item.type === "text") _children = children([]);
|
|
||||||
else {
|
|
||||||
_children = children(
|
|
||||||
item.childs.filter((c) => {
|
|
||||||
if (c.hidden === "all") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p.treeMeta[c.id]) {
|
|
||||||
if (p.treePending) p.treePending.then(meta.render);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}) || []
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meta.className = produceCSS(item, {
|
|
||||||
mode: p.mode,
|
|
||||||
});
|
|
||||||
|
|
||||||
const className = meta.className;
|
|
||||||
const adv = item.adv;
|
|
||||||
|
|
||||||
if (!(adv?.jsBuilt && adv?.js) && meta.comp) {
|
|
||||||
return treeScopeEval(
|
|
||||||
p,
|
|
||||||
id,
|
|
||||||
_children,
|
|
||||||
`render(React.createElement("div",{...props},children));`,
|
|
||||||
_scopeIndex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adv) {
|
|
||||||
if (adv.html) {
|
|
||||||
const html = renderHTML(className, adv);
|
|
||||||
if (html) return html;
|
|
||||||
} else if (adv.jsBuilt && adv.js) {
|
|
||||||
const el = treeScopeEval(p, id, _children, adv.jsBuilt, _scopeIndex);
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const linktag = responsiveVal<FNLinkTag>(item, "linktag", p.mode, {});
|
|
||||||
const isComponent = item.type === "item" && item.component?.id;
|
|
||||||
|
|
||||||
if (linktag && linktag.link && !isComponent) {
|
|
||||||
let href = linktag.link || "";
|
|
||||||
if (href.startsWith("/")) {
|
|
||||||
preload(p, href);
|
|
||||||
if (
|
|
||||||
(location.pathname.startsWith("/preview/") ||
|
|
||||||
location.pathname.startsWith("/site/")) &&
|
|
||||||
["localhost", "127.0.0.1", "prasi.app"].includes(location.hostname)
|
|
||||||
) {
|
|
||||||
const parts = location.pathname.split("/");
|
|
||||||
if (parts.length >= 3) {
|
|
||||||
href = `/${parts[1]}/${parts[2]}${href}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
className: className,
|
|
||||||
href: href,
|
|
||||||
onClick: (e: any) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (href.startsWith("/")) {
|
|
||||||
navigate(href);
|
|
||||||
} else {
|
|
||||||
location.href = href;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (item.type === "text") {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
{...props}
|
|
||||||
dangerouslySetInnerHTML={{ __html: item.html || item.text }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <a {...props}>{_children}</a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.type === "text") {
|
|
||||||
return (
|
|
||||||
<LTextInternal
|
|
||||||
key={item.id}
|
|
||||||
className={className}
|
|
||||||
item={item}
|
|
||||||
p={p}
|
|
||||||
_children={item.html || item.text}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
{/* {isComponent && (
|
|
||||||
<pre className={"text-[9px] font-mono text-black"}>
|
|
||||||
{item.id}-{item.name}
|
|
||||||
</pre>
|
|
||||||
)} */}
|
|
||||||
{_children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const renderHTML = (className: string, adv: FNAdv) => {
|
|
||||||
if (adv.html) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={className}
|
|
||||||
dangerouslySetInnerHTML={{ __html: adv.html }}
|
|
||||||
></div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const PrasiPortal = ({ name }: { name: string }) => {
|
|
||||||
const p = useGlobal(LiveGlobal, "LIVE");
|
|
||||||
const local = useLocal({});
|
|
||||||
|
|
||||||
if (!p.portal[name]) {
|
|
||||||
p.portal[name] = { render: local.render, el: null };
|
|
||||||
} else {
|
|
||||||
p.portal[name].render = local.render;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.portal[name].el;
|
|
||||||
};
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { FC } from "react";
|
|
||||||
import { LItem } from "./l-item";
|
|
||||||
import { LRender } from "./l-render";
|
|
||||||
|
|
||||||
export const LSection: FC<{ id: string }> = ({ id }) => {
|
|
||||||
return (
|
|
||||||
<LRender id={id}>
|
|
||||||
{(childs) =>
|
|
||||||
childs.map((e) => {
|
|
||||||
return <LItem id={e.id} key={e.id} />;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</LRender>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { FC } from "react";
|
|
||||||
import { IText } from "../../../utils/types/text";
|
|
||||||
import { PG } from "../logic/global";
|
|
||||||
import { LRender } from "./l-render";
|
|
||||||
|
|
||||||
export const LText: FC<{
|
|
||||||
id: string;
|
|
||||||
fromProp?: boolean;
|
|
||||||
_scopeIndex?: Record<string, any>;
|
|
||||||
}> = ({ id, fromProp, _scopeIndex }) => {
|
|
||||||
return <LRender id={id} fromProp={fromProp} _scopeIndex={_scopeIndex} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LTextInternal: FC<{
|
|
||||||
className: any;
|
|
||||||
p: PG;
|
|
||||||
item: IText;
|
|
||||||
_children: any;
|
|
||||||
}> = ({ className, item, _children }) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
id={`text-${item.id}`}
|
|
||||||
className={cx(
|
|
||||||
className,
|
|
||||||
css`
|
|
||||||
outline: none;
|
|
||||||
min-width: 3px !important;
|
|
||||||
min-height: 10px !important;
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
dangerouslySetInnerHTML={{ __html: _children || "" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
import { FC, useCallback, useEffect } from "react";
|
|
||||||
import parseUA from "ua-parser-js";
|
|
||||||
import { useGlobal } from "web-utils";
|
|
||||||
import { LPage } from "./elements/l-page";
|
|
||||||
import { LiveGlobal, Loader } from "./logic/global";
|
|
||||||
import { initLive, w } from "./logic/init";
|
|
||||||
import { preload, routeLive } from "./logic/route";
|
|
||||||
import { Loading } from "../../utils/ui/loading";
|
|
||||||
|
|
||||||
export const Live: FC<{
|
|
||||||
domain_or_siteid: string;
|
|
||||||
pathname: string;
|
|
||||||
loader: Loader;
|
|
||||||
liveSync?: boolean;
|
|
||||||
}> = ({ domain_or_siteid, pathname, loader, liveSync }) => {
|
|
||||||
const p = useGlobal(LiveGlobal, "LIVE");
|
|
||||||
p.loader = loader;
|
|
||||||
|
|
||||||
w.preload = (url: string) => {
|
|
||||||
preload(p, url);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (p.site.id) {
|
|
||||||
if (!p.mode && !!p.site.responsive) {
|
|
||||||
if (p.site.responsive === "all") {
|
|
||||||
const parsed = parseUA();
|
|
||||||
p.mode = parsed.device.type === "mobile" ? "mobile" : "desktop";
|
|
||||||
if (localStorage.getItem("prasi-editor-mode")) {
|
|
||||||
p.mode = localStorage.getItem("prasi-editor-mode") as any;
|
|
||||||
}
|
|
||||||
} else if (p.site.responsive === "mobile-only") {
|
|
||||||
p.mode = "mobile";
|
|
||||||
} else if (p.site.responsive === "desktop-only") {
|
|
||||||
p.mode = "desktop";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onResize = useCallback(() => {
|
|
||||||
let newmode = p.mode;
|
|
||||||
if (window.innerWidth < 600) newmode = "mobile";
|
|
||||||
else newmode = "desktop";
|
|
||||||
|
|
||||||
if (newmode !== p.mode) {
|
|
||||||
p.mode = newmode;
|
|
||||||
p.render();
|
|
||||||
}
|
|
||||||
}, [p]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (p.site.id) {
|
|
||||||
window.removeEventListener("resize", onResize);
|
|
||||||
|
|
||||||
if (p.site.responsive === "all") {
|
|
||||||
window.addEventListener("resize", onResize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [p.site.responsive]);
|
|
||||||
|
|
||||||
if (p.status === "init") {
|
|
||||||
initLive(p, domain_or_siteid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.site.id) {
|
|
||||||
routeLive(p, pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p.mode) return <Loading />;
|
|
||||||
else {
|
|
||||||
w.isMobile = p.mode === "mobile";
|
|
||||||
w.isDesktop = p.mode === "desktop";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.status === "not-found")
|
|
||||||
return (
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center flex-col">
|
|
||||||
<div className="text-[40px]">404</div>
|
|
||||||
<div>NOT FOUND</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return <LPage />;
|
|
||||||
};
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
import { createId } from "@paralleldrive/cuid2";
|
|
||||||
import * as Y from "yjs";
|
|
||||||
import { IContent } from "../../../utils/types/general";
|
|
||||||
import { IItem, MItem } from "../../../utils/types/item";
|
|
||||||
import { FNCompDef, FNComponent } from "../../../utils/types/meta-fn";
|
|
||||||
import { IRoot } from "../../../utils/types/root";
|
|
||||||
import { fillID } from "../../editor/tools/fill-id";
|
|
||||||
import { PG } from "./global";
|
|
||||||
import { PRASI_COMPONENT } from "../../../utils/types/render";
|
|
||||||
import { deepClone } from "web-utils";
|
|
||||||
|
|
||||||
export const scanComponent = (
|
|
||||||
item: IRoot | IContent,
|
|
||||||
componentIDS?: Set<string>
|
|
||||||
) => {
|
|
||||||
const ids = componentIDS || new Set();
|
|
||||||
if (!item) return ids;
|
|
||||||
|
|
||||||
if (item.type === "item" && item.component?.id) {
|
|
||||||
ids.add(item.component.id);
|
|
||||||
|
|
||||||
if (item.component.props) {
|
|
||||||
for (const p of Object.values(item.component.props)) {
|
|
||||||
if (p.meta?.type === "content-element" && p.content) {
|
|
||||||
scanComponent(p.content, ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.type !== "text" && item.childs) {
|
|
||||||
for (const c of item.childs) {
|
|
||||||
scanComponent(c, ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const loadComponent = async (
|
|
||||||
p: PG,
|
|
||||||
itemOrID: string | IRoot | IContent
|
|
||||||
) => {
|
|
||||||
const compIds = new Set<string>();
|
|
||||||
let tree: IRoot | IContent = null as any;
|
|
||||||
if (typeof itemOrID !== "string") {
|
|
||||||
tree = itemOrID;
|
|
||||||
} else {
|
|
||||||
await loadComponentById(p, itemOrID);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanComponent(tree, compIds);
|
|
||||||
const promises = [...compIds]
|
|
||||||
.filter((id) => {
|
|
||||||
if (!p.comps.all[id] && !p.comps.pending[id]) return true;
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.map(async (id) => {
|
|
||||||
const comp = await loadComponentById(p, id);
|
|
||||||
await loadComponent(p, comp.content_tree);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (promises.length > 0) {
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadComponentById = async (p: PG, id: string) => {
|
|
||||||
if (!!p.comps.pending[id]) {
|
|
||||||
return await p.comps.pending[id];
|
|
||||||
}
|
|
||||||
const res = p.loader.comp(p, id);
|
|
||||||
p.comps.pending[id] = res;
|
|
||||||
p.comps.all[id] = await res;
|
|
||||||
return p.comps.all[id];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const instantiateComp = (
|
|
||||||
p: PG,
|
|
||||||
item: IItem,
|
|
||||||
mcomp: { item: MItem; type: "m" } | { item: PRASI_COMPONENT; type: "i" },
|
|
||||||
child_ids: Record<string, string>
|
|
||||||
) => {
|
|
||||||
const comp = item.component as FNComponent;
|
|
||||||
|
|
||||||
let target = null as any;
|
|
||||||
let mprops = {} as Record<string, FNCompDef>;
|
|
||||||
if (mcomp.type === "m") {
|
|
||||||
const mitem = mcomp.item;
|
|
||||||
|
|
||||||
mprops = mitem.get("component")?.get("props")?.toJSON() as Record<
|
|
||||||
string,
|
|
||||||
FNCompDef
|
|
||||||
>;
|
|
||||||
|
|
||||||
if (!mprops) {
|
|
||||||
mitem.get("component")?.set("props", new Y.Map() as any);
|
|
||||||
mprops = mitem.get("component")?.get("props")?.toJSON() as any;
|
|
||||||
}
|
|
||||||
target = mitem.toJSON();
|
|
||||||
} else {
|
|
||||||
target = deepClone(mcomp.item.content_tree);
|
|
||||||
mprops = target.component.props || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let nitem = {};
|
|
||||||
nitem = fillID(target, (i) => {
|
|
||||||
if (!i.originalId) {
|
|
||||||
i.originalId = i.id;
|
|
||||||
}
|
|
||||||
let newid = i.id;
|
|
||||||
if (child_ids[i.originalId]) {
|
|
||||||
newid = child_ids[i.originalId];
|
|
||||||
} else {
|
|
||||||
newid = createId();
|
|
||||||
}
|
|
||||||
|
|
||||||
i.id = newid;
|
|
||||||
child_ids[i.originalId] = newid;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}) as IItem;
|
|
||||||
|
|
||||||
const props: any = {};
|
|
||||||
for (const [k, v] of Object.entries(mprops)) {
|
|
||||||
props[k] = v;
|
|
||||||
if (comp.props[k]) {
|
|
||||||
props[k].value = comp.props[k].value;
|
|
||||||
props[k].valueBuilt = comp.props[k].valueBuilt;
|
|
||||||
props[k].content = comp.props[k].content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...nitem,
|
|
||||||
id: item.id,
|
|
||||||
originalId: item.originalId || (nitem as IItem).originalId,
|
|
||||||
component: { ...comp, props },
|
|
||||||
} as IItem;
|
|
||||||
};
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
import { createRouter } from "radix3";
|
|
||||||
import { FC, ReactNode } from "react";
|
|
||||||
import { CompDoc } from "../../../base/global/content-editor";
|
|
||||||
import { IContent, MPage } from "../../../utils/types/general";
|
|
||||||
import { IItem, MItem } from "../../../utils/types/item";
|
|
||||||
import { PRASI_COMPONENT } from "../../../utils/types/render";
|
|
||||||
import { IRoot } from "../../../utils/types/root";
|
|
||||||
import { ISection } from "../../../utils/types/section";
|
|
||||||
import { IText } from "../../../utils/types/text";
|
|
||||||
|
|
||||||
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
|
||||||
export type ItemMeta = {
|
|
||||||
item: IContent;
|
|
||||||
scope?: any;
|
|
||||||
indexedScope: Record<string, any>;
|
|
||||||
comp?: {
|
|
||||||
id: string;
|
|
||||||
propval?: any;
|
|
||||||
child_ids: Record<string, string>;
|
|
||||||
};
|
|
||||||
className?: string;
|
|
||||||
parent_id: string;
|
|
||||||
parent_comp?: WithRequired<ItemMeta, "comp"> & { item: IItem };
|
|
||||||
memoize?: Record<string, {
|
|
||||||
Local: FC<any>;
|
|
||||||
PassProp: FC<any>;
|
|
||||||
}>;
|
|
||||||
isLayout: boolean;
|
|
||||||
render?: () => void;
|
|
||||||
mounted?: boolean;
|
|
||||||
pendingRender?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LPage = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
content_tree?: IRoot;
|
|
||||||
js: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LSite = {
|
|
||||||
id: string;
|
|
||||||
api_url: string;
|
|
||||||
api_prasi: {
|
|
||||||
port: string;
|
|
||||||
db: string;
|
|
||||||
};
|
|
||||||
responsive: "all" | "mobile-only" | "desktop-only";
|
|
||||||
domain: string;
|
|
||||||
name: string;
|
|
||||||
js: string;
|
|
||||||
js_compiled: string;
|
|
||||||
layout?: ISection;
|
|
||||||
layout_id?: string;
|
|
||||||
cgroup_ids?: string[];
|
|
||||||
config?: any;
|
|
||||||
};
|
|
||||||
export type Loader = {
|
|
||||||
site: (
|
|
||||||
p: PG,
|
|
||||||
where: { type: "domain"; domain: string } | { type: "siteid"; id: string }
|
|
||||||
) => Promise<LSite | null>;
|
|
||||||
page: (p: PG, id: string) => Promise<LPage | null>;
|
|
||||||
pages: (p: PG, site_id: string) => Promise<LPage[]>;
|
|
||||||
npm: (p: PG, type: "site" | "page", id: string) => string;
|
|
||||||
comp: (p: PG, id: string) => Promise<PRASI_COMPONENT>;
|
|
||||||
};
|
|
||||||
export const LiveGlobal = {
|
|
||||||
liveSync: {
|
|
||||||
ws: null as null | WebSocket,
|
|
||||||
init: false,
|
|
||||||
decompress: null as null | ((buf: Uint8Array) => Uint8Array),
|
|
||||||
},
|
|
||||||
prod: false,
|
|
||||||
loader: undefined as unknown as Loader,
|
|
||||||
mode: "" as "desktop" | "mobile",
|
|
||||||
layout: {
|
|
||||||
section: null as null | ISection,
|
|
||||||
content: null as null | IItem | IText,
|
|
||||||
},
|
|
||||||
status: "init" as
|
|
||||||
| "init"
|
|
||||||
| "loading"
|
|
||||||
| "reload"
|
|
||||||
| "ready"
|
|
||||||
| "not-found"
|
|
||||||
| "error"
|
|
||||||
| "tree-rebuild",
|
|
||||||
site: {
|
|
||||||
id: "",
|
|
||||||
api_url: "",
|
|
||||||
api_prasi: {
|
|
||||||
port: "",
|
|
||||||
db: "",
|
|
||||||
},
|
|
||||||
responsive: "all" as "all" | "mobile-only" | "desktop-only",
|
|
||||||
domain: "",
|
|
||||||
name: "",
|
|
||||||
js: "",
|
|
||||||
js_compiled: "",
|
|
||||||
} as LSite,
|
|
||||||
page: null as null | LPage,
|
|
||||||
mpage: null as null | MPage,
|
|
||||||
mpageLoaded: null as null | ((mpage: MPage) => void),
|
|
||||||
pagePreload: {} as Record<string, true>,
|
|
||||||
pages: {} as Record<string, LPage>,
|
|
||||||
route: createRouter<{
|
|
||||||
id: string;
|
|
||||||
url: string;
|
|
||||||
}>(),
|
|
||||||
treePending: null as null | Promise<void>,
|
|
||||||
treeMeta: {} as Record<string, ItemMeta>,
|
|
||||||
cachedParentID: {} as Record<string, string>,
|
|
||||||
portal: {} as Record<string, { el?: ReactNode; render: () => void }>,
|
|
||||||
comps: {
|
|
||||||
pending: {} as Record<string, Promise<PRASI_COMPONENT>>,
|
|
||||||
resolve: {} as Record<string, (comp: PRASI_COMPONENT) => void>,
|
|
||||||
all: {} as Record<string, PRASI_COMPONENT>,
|
|
||||||
},
|
|
||||||
script: {
|
|
||||||
db: null as any,
|
|
||||||
api: null as any,
|
|
||||||
},
|
|
||||||
compInstance: {} as Record<string, Record<string, string>>,
|
|
||||||
ws: null as null | WebSocket,
|
|
||||||
wsRetry: {
|
|
||||||
fast: false,
|
|
||||||
localIP: false,
|
|
||||||
disabled: false,
|
|
||||||
reconnecting: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PG = typeof LiveGlobal & { render: () => void };
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
import { createRouter } from "radix3";
|
|
||||||
import { validate } from "uuid";
|
|
||||||
import { apiProxy } from "../../../base/load/api/api-proxy";
|
|
||||||
import { dbProxy } from "../../../base/load/db/db-proxy";
|
|
||||||
import importModule from "../../editor/tools/dynamic-import";
|
|
||||||
import { LSite, PG } from "./global";
|
|
||||||
import { validateLayout } from "./layout";
|
|
||||||
import { registerMobile } from "./mobile";
|
|
||||||
|
|
||||||
export const w = window as unknown as {
|
|
||||||
basepath: string;
|
|
||||||
navigateOverride: (s: string) => string;
|
|
||||||
isEditor: boolean;
|
|
||||||
isMobile: boolean;
|
|
||||||
isLayout: boolean;
|
|
||||||
apiHeaders: any;
|
|
||||||
isDesktop: boolean;
|
|
||||||
exports: any;
|
|
||||||
params: any;
|
|
||||||
apiurl: string;
|
|
||||||
preload: (path: string) => void;
|
|
||||||
mobile?: ReturnType<typeof registerMobile>;
|
|
||||||
externalAPI: {
|
|
||||||
mode: "dev" | "prod";
|
|
||||||
devUrl: string;
|
|
||||||
prodUrl: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initLive = async (p: PG, domain_or_siteid: string) => {
|
|
||||||
if (p.status === "init") {
|
|
||||||
p.status = "loading";
|
|
||||||
|
|
||||||
if (w.mobile) {
|
|
||||||
w.mobile.bind(p);
|
|
||||||
w.mobile.send({ type: "ready" });
|
|
||||||
}
|
|
||||||
|
|
||||||
w.isEditor = false;
|
|
||||||
w.isLayout = true;
|
|
||||||
w.apiHeaders = {};
|
|
||||||
|
|
||||||
w.navigateOverride = (_href) => {
|
|
||||||
if (_href && _href.startsWith("/")) {
|
|
||||||
if (w.basepath.length > 1) {
|
|
||||||
_href = `${w.basepath}${_href}`;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
location.hostname === "prasi.app" ||
|
|
||||||
location.hostname === "prasi.avolut.com" ||
|
|
||||||
location.hostname.includes("ngrok") ||
|
|
||||||
location.hostname === "localhost" ||
|
|
||||||
location.hostname === "127.0.0.1" ||
|
|
||||||
location.hostname === "10.0.2.2" // android localhost
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
location.pathname.startsWith("/live") &&
|
|
||||||
!_href.startsWith("/live")
|
|
||||||
) {
|
|
||||||
const patharr = location.pathname.split("/");
|
|
||||||
_href = `/live/${patharr[2]}${_href}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _href;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** load site */
|
|
||||||
let site = null as null | LSite;
|
|
||||||
site = await p.loader.site(
|
|
||||||
p,
|
|
||||||
validate(domain_or_siteid)
|
|
||||||
? { type: "siteid", id: domain_or_siteid }
|
|
||||||
: { type: "domain", domain: domain_or_siteid }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (site) {
|
|
||||||
/** import site module */
|
|
||||||
w.exports = {};
|
|
||||||
|
|
||||||
if (site.cgroup_ids) {
|
|
||||||
for (const id of site.cgroup_ids) {
|
|
||||||
await importModule(p.loader.npm(p, "site", id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await importModule(p.loader.npm(p, "site", site.id));
|
|
||||||
|
|
||||||
p.site.id = site.id;
|
|
||||||
p.site.layout = site.layout;
|
|
||||||
p.site.js = site.js_compiled || "";
|
|
||||||
p.site.responsive = site.responsive as any;
|
|
||||||
|
|
||||||
await validateLayout(p);
|
|
||||||
|
|
||||||
p.site.api_url = site.config.api_url || "";
|
|
||||||
|
|
||||||
w.apiurl = p.site.api_url;
|
|
||||||
|
|
||||||
let pages = await p.loader.pages(p, site.id);
|
|
||||||
|
|
||||||
/** execute site module */
|
|
||||||
const exec = (fn: string, scopes: any) => {
|
|
||||||
if (p) {
|
|
||||||
if (p.site.api_url) {
|
|
||||||
scopes["api"] = apiProxy(p.site.api_url);
|
|
||||||
scopes["db"] = dbProxy(p.site.api_url);
|
|
||||||
}
|
|
||||||
if (!w.params) {
|
|
||||||
w.params = {};
|
|
||||||
}
|
|
||||||
scopes.params = w.params;
|
|
||||||
scopes.module = {};
|
|
||||||
const f = new Function(...Object.keys(scopes), fn);
|
|
||||||
const res = f(...Object.values(scopes));
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
const scope = {
|
|
||||||
types: {},
|
|
||||||
exports: w.exports,
|
|
||||||
load: importModule,
|
|
||||||
render: p.render,
|
|
||||||
module: {
|
|
||||||
exports: {} as any,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
exec(p.site.js, scope);
|
|
||||||
if (scope.module.exports) {
|
|
||||||
for (const [k, v] of Object.entries(scope.module.exports)) {
|
|
||||||
w.exports[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** create router */
|
|
||||||
p.pages = {};
|
|
||||||
p.route = createRouter({ strictTrailingSlash: false });
|
|
||||||
if (pages && pages.length > 0) {
|
|
||||||
for (const page of pages) {
|
|
||||||
p.pages[page.id] = page;
|
|
||||||
p.route.insert(page.url, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.status = "ready";
|
|
||||||
p.render();
|
|
||||||
} else {
|
|
||||||
p.status = "not-found";
|
|
||||||
p.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import { IItem } from "../../../utils/types/item";
|
|
||||||
import { IText } from "../../../utils/types/text";
|
|
||||||
import { LSite, PG } from "./global";
|
|
||||||
|
|
||||||
export const validateLayout = async (p: {
|
|
||||||
site: LSite;
|
|
||||||
layout: PG["layout"];
|
|
||||||
}) => {
|
|
||||||
if (p.site.layout) {
|
|
||||||
p.layout.section = p.site.layout;
|
|
||||||
|
|
||||||
for (const child of p.layout.section.childs) {
|
|
||||||
const found = await walk(child);
|
|
||||||
if (found) {
|
|
||||||
p.layout.content = found;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const walk = async (
|
|
||||||
item: IItem | IText
|
|
||||||
): Promise<IItem | IText | undefined> => {
|
|
||||||
if (item.name === "content") {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
if (item.type === "item" && !item.component?.id) {
|
|
||||||
for (const c of item.childs) {
|
|
||||||
const found = await walk(c);
|
|
||||||
if (found) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
import { w } from "../../../utils/types/general";
|
|
||||||
import { PG } from "./global";
|
|
||||||
|
|
||||||
type NOTIF_ARG = {
|
|
||||||
user_id: any;
|
|
||||||
body: string;
|
|
||||||
title: string;
|
|
||||||
data?: any;
|
|
||||||
};
|
|
||||||
export const registerMobile = () => {
|
|
||||||
const default_mobile = {
|
|
||||||
send: () => {},
|
|
||||||
bind: (p: PG) => {},
|
|
||||||
notif: {
|
|
||||||
register: (user_id: string) => {},
|
|
||||||
send: async (data: NOTIF_ARG) => {
|
|
||||||
const p = getP();
|
|
||||||
if (p) {
|
|
||||||
return await p.script.api._notif("send", {
|
|
||||||
type: "send",
|
|
||||||
id:
|
|
||||||
typeof data.user_id === "string"
|
|
||||||
? data.user_id
|
|
||||||
: data.user_id.toString(),
|
|
||||||
body: data.body,
|
|
||||||
title: data.title,
|
|
||||||
data: data.data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onTap: (data: NOTIF_ARG) => {},
|
|
||||||
onReceive: (data: NOTIF_ARG) => {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let config = { notif_token: "", p: null as null | PG };
|
|
||||||
const getP = () => {
|
|
||||||
const p = config.p;
|
|
||||||
if (p && p.site && p.site.api_url) {
|
|
||||||
const api = w.prasiApi[p.site.api_url];
|
|
||||||
if (
|
|
||||||
api &&
|
|
||||||
api.apiEntry &&
|
|
||||||
api.apiEntry._notif &&
|
|
||||||
p.script &&
|
|
||||||
p.script.api
|
|
||||||
) {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (window.parent) {
|
|
||||||
window.addEventListener("message", async ({ data: raw }) => {
|
|
||||||
if (typeof raw === "object" && raw.mobile) {
|
|
||||||
const data = raw as unknown as
|
|
||||||
| {
|
|
||||||
type: "notification-token";
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
| { type: "notification-tap"; notif: NOTIF_ARG }
|
|
||||||
| { type: "notification-receive"; notif: NOTIF_ARG };
|
|
||||||
|
|
||||||
const waitUntil = async (fn: () => boolean) => {
|
|
||||||
if (!notifObject.notif.onTap) {
|
|
||||||
let ival = null as any;
|
|
||||||
let i = 0;
|
|
||||||
await new Promise(() => {
|
|
||||||
ival = setInterval(() => {
|
|
||||||
i++;
|
|
||||||
if (i > 20) {
|
|
||||||
clearInterval(ival);
|
|
||||||
}
|
|
||||||
if (fn()) {
|
|
||||||
clearInterval(ival);
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (data.type) {
|
|
||||||
case "notification-token":
|
|
||||||
config.notif_token = data.token;
|
|
||||||
break;
|
|
||||||
case "notification-tap":
|
|
||||||
if (!notifObject.notif.onTap) {
|
|
||||||
waitUntil(() => {
|
|
||||||
if (notifObject.notif.onTap) {
|
|
||||||
notifObject.notif.onTap(data.notif);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notifObject.notif.onTap) {
|
|
||||||
notifObject.notif.onTap(data.notif);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "notification-receive":
|
|
||||||
if (!notifObject.notif.onReceive) {
|
|
||||||
waitUntil(() => {
|
|
||||||
if (notifObject.notif.onReceive) {
|
|
||||||
notifObject.notif.onReceive(data.notif);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (notifObject.notif.onReceive) {
|
|
||||||
notifObject.notif.onReceive(data.notif);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const notifObject = {
|
|
||||||
send: (msg: { type: "ready" }) => {
|
|
||||||
window.parent.postMessage({ mobile: true, ...msg }, "*");
|
|
||||||
},
|
|
||||||
bind(p: PG) {
|
|
||||||
config.p = p;
|
|
||||||
},
|
|
||||||
config,
|
|
||||||
notif: {
|
|
||||||
register: async (user_id: any) => {
|
|
||||||
const p = getP();
|
|
||||||
if (p) {
|
|
||||||
return await p.script.api._notif("register", {
|
|
||||||
type: "register",
|
|
||||||
id: typeof user_id === "string" ? user_id : user_id.toString(),
|
|
||||||
token: config.notif_token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
send: async (data: NOTIF_ARG) => {
|
|
||||||
const p = getP();
|
|
||||||
if (p) {
|
|
||||||
return await p.script.api._notif("send", {
|
|
||||||
type: "send",
|
|
||||||
id:
|
|
||||||
typeof data.user_id === "string"
|
|
||||||
? data.user_id
|
|
||||||
: data.user_id.toString(),
|
|
||||||
body: data.body,
|
|
||||||
title: data.title,
|
|
||||||
data: data.data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onTap: null as null | typeof default_mobile.notif.onTap,
|
|
||||||
onReceive: null as null | typeof default_mobile.notif.onReceive,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return notifObject;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...default_mobile,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
import { w } from "../../../utils/types/general";
|
|
||||||
import importModule from "../../editor/tools/dynamic-import";
|
|
||||||
import { loadComponent } from "./comp";
|
|
||||||
import { LPage, PG } from "./global";
|
|
||||||
import { rebuildTree } from "./tree-logic";
|
|
||||||
|
|
||||||
export const routeLive = (p: PG, pathname: string) => {
|
|
||||||
if (p.status !== "loading" && p.status !== "not-found") {
|
|
||||||
if (!w.params) w.params = {};
|
|
||||||
let page_id = w.params.page_id;
|
|
||||||
|
|
||||||
if (!page_id) {
|
|
||||||
const found = p.route.lookup(pathname);
|
|
||||||
if (!found) {
|
|
||||||
p.status = "not-found";
|
|
||||||
} else {
|
|
||||||
if (found.params) {
|
|
||||||
for (const [k, v] of Object.entries(found.params)) {
|
|
||||||
w.params[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page_id = found.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page_id) {
|
|
||||||
(window as any).pageid = page_id;
|
|
||||||
const promises: Promise<void>[] = [];
|
|
||||||
if (page_id !== p.page?.id) {
|
|
||||||
p.page = p.pages[page_id];
|
|
||||||
p.treeMeta = {};
|
|
||||||
p.portal = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p.page || !p.page.content_tree) {
|
|
||||||
promises.push(loadNpmPage(p, page_id));
|
|
||||||
promises.push(loadPage(p, page_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promises.length > 0) {
|
|
||||||
p.status = "loading";
|
|
||||||
Promise.all(promises).then(async () => {
|
|
||||||
p.page = p.pages[page_id];
|
|
||||||
if (p.page) {
|
|
||||||
await pageLoaded(p);
|
|
||||||
p.render();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (!firstRender[page_id]) {
|
|
||||||
firstRender[page_id] = true;
|
|
||||||
pageLoaded(p).then(p.render);
|
|
||||||
} else {
|
|
||||||
pageLoaded(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const firstRender = {} as Record<string, true>;
|
|
||||||
|
|
||||||
const pageLoaded = async (p: PG) => {
|
|
||||||
if (p.page) {
|
|
||||||
await rebuildTree(p, { render: false, note: "render", reset: false });
|
|
||||||
p.status = "ready";
|
|
||||||
} else {
|
|
||||||
p.status = "not-found";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const preload = async (p: PG, pathname: string) => {
|
|
||||||
const found = p.route.lookup(pathname);
|
|
||||||
if (found) {
|
|
||||||
if (
|
|
||||||
(!p.pages[found.id] ||
|
|
||||||
(p.pages[found.id] && !p.pages[found.id].content_tree)) &&
|
|
||||||
!p.pagePreload[found.id]
|
|
||||||
) {
|
|
||||||
p.pagePreload[found.id] = true;
|
|
||||||
const dbpage = await p.loader.page(p, found.id);
|
|
||||||
if (dbpage) {
|
|
||||||
p.pages[dbpage.id] = dbpage;
|
|
||||||
const page = p.pages[dbpage.id];
|
|
||||||
if (page && page.content_tree) {
|
|
||||||
await loadComponent(p, page.content_tree);
|
|
||||||
}
|
|
||||||
delete p.pagePreload[found.id];
|
|
||||||
await loadNpmPage(p, dbpage.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const npmPageLoaded = {} as Record<string, true>;
|
|
||||||
const loadNpmPage = async (p: PG, id: string) => {
|
|
||||||
try {
|
|
||||||
if (!npmPageLoaded[id]) {
|
|
||||||
npmPageLoaded[id] = true;
|
|
||||||
if (typeof window.exports === "undefined") {
|
|
||||||
window.exports = {};
|
|
||||||
}
|
|
||||||
const npmurl = p.loader.npm(p, "page", id);
|
|
||||||
if (npmurl) {
|
|
||||||
await importModule(npmurl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loading = {} as Record<string, Promise<LPage | null>>;
|
|
||||||
|
|
||||||
const loadPage = async (p: PG, id: string) => {
|
|
||||||
if (!loading[id]) {
|
|
||||||
loading[id] = p.loader.page(p, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = await loading[id];
|
|
||||||
|
|
||||||
if (page) {
|
|
||||||
p.pages[page.id] = {
|
|
||||||
id: page.id,
|
|
||||||
url: page.url,
|
|
||||||
name: page.name,
|
|
||||||
content_tree: page.content_tree as any,
|
|
||||||
js: (page as any).js_compiled as any,
|
|
||||||
};
|
|
||||||
|
|
||||||
const cur = p.pages[page.id];
|
|
||||||
if (cur && cur.content_tree) {
|
|
||||||
await loadComponent(p, cur.content_tree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
@ -1,189 +0,0 @@
|
||||||
import { produceCSS } from "../../../utils/css/gen";
|
|
||||||
import { IContent } from "../../../utils/types/general";
|
|
||||||
import { IItem } from "../../../utils/types/item";
|
|
||||||
import { instantiateComp, loadComponent } from "./comp";
|
|
||||||
import { ItemMeta, LiveGlobal, PG } from "./global";
|
|
||||||
|
|
||||||
export const rebuildTree = async (
|
|
||||||
p: PG,
|
|
||||||
_: { note: string; render?: boolean; reset?: boolean }
|
|
||||||
) => {
|
|
||||||
if (!p.page?.content_tree) return;
|
|
||||||
|
|
||||||
p.treePending = new Promise<void>(async (resolve) => {
|
|
||||||
const treeMeta = p.treeMeta;
|
|
||||||
if (p.page) {
|
|
||||||
let childs = Object.values(p.page.content_tree?.childs || []);
|
|
||||||
if (
|
|
||||||
p.layout.section &&
|
|
||||||
p.layout.content &&
|
|
||||||
!p.page?.name.startsWith("layout:")
|
|
||||||
) {
|
|
||||||
childs = [p.layout.section];
|
|
||||||
|
|
||||||
p.layout.content.type = "item";
|
|
||||||
if (p.layout.content.type === "item") {
|
|
||||||
p.layout.content.childs = p.page.content_tree?.childs.map((e) => ({
|
|
||||||
...e,
|
|
||||||
type: "item",
|
|
||||||
})) as IItem[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
childs.map(async (item, idx) => {
|
|
||||||
await walk(p, {
|
|
||||||
treeMeta,
|
|
||||||
item,
|
|
||||||
parent_id: "root",
|
|
||||||
idx,
|
|
||||||
isLayout: !!(p.layout.section && p.layout.content),
|
|
||||||
});
|
|
||||||
}) || []
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
p.treePending = null;
|
|
||||||
|
|
||||||
if (_.render !== false) {
|
|
||||||
p.render();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await p.treePending;
|
|
||||||
};
|
|
||||||
|
|
||||||
const walk = async (
|
|
||||||
p: PG,
|
|
||||||
val: {
|
|
||||||
treeMeta: (typeof LiveGlobal)["treeMeta"];
|
|
||||||
item?: IContent;
|
|
||||||
isLayout: boolean;
|
|
||||||
parent_id: string;
|
|
||||||
idx: number;
|
|
||||||
parent_comp?: ItemMeta["parent_comp"];
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const treeMeta = val.treeMeta;
|
|
||||||
let item = val.item as IContent;
|
|
||||||
|
|
||||||
if (item.hidden === "all") return;
|
|
||||||
|
|
||||||
if (val.parent_comp) {
|
|
||||||
const pchild_ids = val.parent_comp.comp?.child_ids;
|
|
||||||
|
|
||||||
if (pchild_ids && item.originalId) {
|
|
||||||
if (pchild_ids[item.originalId]) {
|
|
||||||
item.id = pchild_ids[item.originalId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
let comp: ItemMeta["comp"] = undefined as any;
|
|
||||||
|
|
||||||
if (item.type === "item" && item.component?.id) {
|
|
||||||
comp = {
|
|
||||||
id: item.component.id,
|
|
||||||
child_ids: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const meta: ItemMeta = {
|
|
||||||
item,
|
|
||||||
parent_id: p.cachedParentID[item.id] || val.parent_id,
|
|
||||||
parent_comp: val.parent_comp as any,
|
|
||||||
className: produceCSS(item, {
|
|
||||||
mode: p.mode,
|
|
||||||
}),
|
|
||||||
comp,
|
|
||||||
indexedScope: treeMeta[item.id] ? treeMeta[item.id].indexedScope : {},
|
|
||||||
isLayout: val.isLayout,
|
|
||||||
};
|
|
||||||
|
|
||||||
treeMeta[meta.item.id] = meta;
|
|
||||||
|
|
||||||
if (item.type === "item" && item.component?.id) {
|
|
||||||
const cid = item.component.id;
|
|
||||||
let comp = p.comps.all[cid];
|
|
||||||
if (!comp) {
|
|
||||||
await loadComponent(p, cid);
|
|
||||||
comp = p.comps.all[cid];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comp) {
|
|
||||||
if (!p.compInstance[item.id]) {
|
|
||||||
p.compInstance[item.id] = {};
|
|
||||||
}
|
|
||||||
const child_ids = p.compInstance[item.id];
|
|
||||||
const itemnew = instantiateComp(
|
|
||||||
p,
|
|
||||||
item,
|
|
||||||
{ type: "i", item: comp },
|
|
||||||
child_ids
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(itemnew)) {
|
|
||||||
if (k !== "id") (item as any)[k] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cprops = comp.content_tree.component?.props;
|
|
||||||
const iprops = item.component.props;
|
|
||||||
|
|
||||||
if (cprops && iprops) {
|
|
||||||
for (const [name, mprop] of Object.entries(cprops)) {
|
|
||||||
const jprop = iprops[name];
|
|
||||||
|
|
||||||
if (jprop) {
|
|
||||||
if (mprop.meta?.type === "content-element") {
|
|
||||||
let icontent = jprop.content;
|
|
||||||
|
|
||||||
if (icontent)
|
|
||||||
await walk(p, {
|
|
||||||
treeMeta,
|
|
||||||
item: icontent,
|
|
||||||
parent_id: item.id,
|
|
||||||
parent_comp: val.parent_comp,
|
|
||||||
idx: mprop.idx,
|
|
||||||
isLayout: meta.isLayout,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
item.childs.map(async (child, idx) => {
|
|
||||||
return await walk(p, {
|
|
||||||
treeMeta,
|
|
||||||
item: child,
|
|
||||||
parent_comp: meta as any,
|
|
||||||
parent_id: item.id,
|
|
||||||
idx,
|
|
||||||
isLayout: meta.isLayout,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (item.type !== "text" && Array.isArray(item.childs)) {
|
|
||||||
await Promise.all(
|
|
||||||
item.childs.map(async (child, idx) => {
|
|
||||||
let isLayout = false;
|
|
||||||
if (meta.isLayout && item.name !== "content") {
|
|
||||||
isLayout = true;
|
|
||||||
}
|
|
||||||
return await walk(p, {
|
|
||||||
treeMeta,
|
|
||||||
idx,
|
|
||||||
item: child,
|
|
||||||
parent_comp: val.parent_comp,
|
|
||||||
parent_id: item.id || "",
|
|
||||||
isLayout,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
import { FC } from "react";
|
|
||||||
import { apiProxy } from "../../../base/load/api/api-proxy";
|
|
||||||
import { dbProxy } from "../../../base/load/db/db-proxy";
|
|
||||||
import { FNCompDef } from "../../../utils/types/meta-fn";
|
|
||||||
import { LItem } from "../elements/l-item";
|
|
||||||
import { PG } from "./global";
|
|
||||||
import { extractNavigate, preload } from "./route";
|
|
||||||
import { mergeScopeUpwards } from "./tree-scope";
|
|
||||||
|
|
||||||
export type PropCompFC = FC<{}>;
|
|
||||||
|
|
||||||
const jsxProps = {} as Record<string, any>;
|
|
||||||
export const treePropEval = (
|
|
||||||
p: PG,
|
|
||||||
id: string,
|
|
||||||
cprops: [string, FNCompDef][],
|
|
||||||
_scopeIndex?: Record<string, string>
|
|
||||||
) => {
|
|
||||||
const meta = p.treeMeta[id];
|
|
||||||
|
|
||||||
if (meta.item.type === "item" && meta.item.component) {
|
|
||||||
if (p.site.api_url) {
|
|
||||||
if (!p.script.db) p.script.db = dbProxy(p.site.api_url);
|
|
||||||
if (!p.script.api) p.script.api = apiProxy(p.site.api_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = meta.item.component.props;
|
|
||||||
|
|
||||||
const w = window as any;
|
|
||||||
const finalScope = mergeScopeUpwards(p, id, { _scopeIndex });
|
|
||||||
|
|
||||||
const args = {
|
|
||||||
...w.exports,
|
|
||||||
...finalScope,
|
|
||||||
db: p.script.db,
|
|
||||||
api: p.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(p, 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 = p.treeMeta[prop.content.id];
|
|
||||||
if (meta) {
|
|
||||||
meta.parent_id = parent_id;
|
|
||||||
p.cachedParentID[prop.content.id] = parent_id;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LItem
|
|
||||||
id={prop.content.id}
|
|
||||||
fromProp={true}
|
|
||||||
_scopeIndex={_scopeIndex}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
value = jsxProps[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result[name] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,369 +0,0 @@
|
||||||
import hash_sum from "hash-sum";
|
|
||||||
import { FC, ReactNode, Suspense, useEffect, useState } from "react";
|
|
||||||
import { deepClone } from "web-utils";
|
|
||||||
import { apiProxy } from "../../../base/load/api/api-proxy";
|
|
||||||
import { dbProxy } from "../../../base/load/db/db-proxy";
|
|
||||||
import { ErrorBox } from "../../editor/elements/e-error";
|
|
||||||
import { ItemMeta, PG } from "./global";
|
|
||||||
import { extractNavigate, preload } from "./route";
|
|
||||||
|
|
||||||
export const JS_DEBUG = false;
|
|
||||||
|
|
||||||
export const treeScopeEval = (
|
|
||||||
p: PG,
|
|
||||||
id: string,
|
|
||||||
children: ReactNode,
|
|
||||||
js: string,
|
|
||||||
_scopeIndex?: Record<string, any>
|
|
||||||
) => {
|
|
||||||
const meta = p.treeMeta[id];
|
|
||||||
const className = meta.className;
|
|
||||||
|
|
||||||
let item = meta.item;
|
|
||||||
|
|
||||||
const adv = item.adv;
|
|
||||||
let args = {};
|
|
||||||
try {
|
|
||||||
if (!meta.memoize) {
|
|
||||||
meta.memoize = {};
|
|
||||||
}
|
|
||||||
const memoizeKey = hash_sum(_scopeIndex) || "default";
|
|
||||||
if (!meta.memoize[memoizeKey]) {
|
|
||||||
meta.memoize[memoizeKey] = {
|
|
||||||
Local: createLocal(p, id, _scopeIndex),
|
|
||||||
PassProp: createPassProp(p, id, _scopeIndex),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof adv?.js === "string") {
|
|
||||||
const navs = extractNavigate(adv.js || "");
|
|
||||||
if (navs.length > 0) {
|
|
||||||
navs.map((nav) => preload(p, nav));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare args
|
|
||||||
if (p.site.api_url) {
|
|
||||||
if (!p.script.db) p.script.db = dbProxy(p.site.api_url);
|
|
||||||
if (!p.script.api) p.script.api = apiProxy(p.site.api_url);
|
|
||||||
}
|
|
||||||
const w = window as any;
|
|
||||||
|
|
||||||
const finalScope = mergeScopeUpwards(p, 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: p.script.db,
|
|
||||||
api: p.script.api,
|
|
||||||
children,
|
|
||||||
props: { className },
|
|
||||||
useEffect: useEffect,
|
|
||||||
render: (jsx: ReactNode) => {
|
|
||||||
output.jsx = (
|
|
||||||
<ErrorBox>
|
|
||||||
<Suspense>{jsx}</Suspense>
|
|
||||||
</ErrorBox>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 ` +
|
|
||||||
((adv?.js || "") as any)
|
|
||||||
).trim()
|
|
||||||
);
|
|
||||||
console.warn(`Available var:`, args, `\n\n`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return output.jsx;
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
console.warn(
|
|
||||||
(
|
|
||||||
`ERROR in ${item.type} [${item.name}]:\n ` + ((adv?.js || "") as any)
|
|
||||||
).trim()
|
|
||||||
);
|
|
||||||
console.warn(`Available var:`, args, `\n\n`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mergeScopeUpwards = (
|
|
||||||
p: PG,
|
|
||||||
id: string,
|
|
||||||
opt?: {
|
|
||||||
_scopeIndex?: Record<string, any>;
|
|
||||||
debug?: boolean;
|
|
||||||
each?: (meta: ItemMeta, values: Record<string, any>) => boolean;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const meta = p.treeMeta[id];
|
|
||||||
|
|
||||||
if (!meta.scope) {
|
|
||||||
meta.scope = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let cur = meta;
|
|
||||||
const finalScope: any = {};
|
|
||||||
while (cur) {
|
|
||||||
let scope = null;
|
|
||||||
|
|
||||||
let indexedScope = null;
|
|
||||||
|
|
||||||
if (cur.indexedScope && opt?._scopeIndex) {
|
|
||||||
const idx = opt._scopeIndex[cur.item.id];
|
|
||||||
|
|
||||||
if (typeof idx !== "undefined" && cur.indexedScope[idx]) {
|
|
||||||
indexedScope = cur.indexedScope[idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexedScope || cur.scope || cur.comp?.propval) {
|
|
||||||
scope = { ...cur.scope, ...indexedScope, ...cur.comp?.propval };
|
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(scope)) {
|
|
||||||
if (typeof finalScope[k] === "undefined") finalScope[k] = v;
|
|
||||||
}
|
|
||||||
if (opt?.each) {
|
|
||||||
if (!opt.each(cur, scope)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur = p.treeMeta[cur.parent_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalScope;
|
|
||||||
};
|
|
||||||
|
|
||||||
const modifyChildIndex = (
|
|
||||||
child: ReactNode | ReactNode[],
|
|
||||||
_scopeIndex: Record<string, any>
|
|
||||||
) => {
|
|
||||||
if (Array.isArray(child)) {
|
|
||||||
const childs: any[] = [];
|
|
||||||
for (const c of child) {
|
|
||||||
childs.push(modifyChildIndex(c, _scopeIndex));
|
|
||||||
}
|
|
||||||
return childs;
|
|
||||||
}
|
|
||||||
if (typeof child === "object" && child) {
|
|
||||||
return { ...child, props: { ...(child as any).props, _scopeIndex } };
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createPassProp = (
|
|
||||||
p: PG,
|
|
||||||
id: string,
|
|
||||||
_existingScopeIndex?: Record<string, any>
|
|
||||||
) => {
|
|
||||||
return function (
|
|
||||||
arg: Record<string, any> & { children: ReactNode; idx?: any }
|
|
||||||
) {
|
|
||||||
const meta = p.treeMeta[id];
|
|
||||||
|
|
||||||
if (typeof arg.idx !== "undefined" && meta && meta.item && meta.item.id) {
|
|
||||||
meta.indexedScope[arg.idx] = {};
|
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(arg)) {
|
|
||||||
if (k === "children") continue;
|
|
||||||
meta.indexedScope[arg.idx][k] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scopeIndex = { ..._existingScopeIndex, [meta.item.id]: arg.idx };
|
|
||||||
|
|
||||||
if (!meta.scope) {
|
|
||||||
meta.scope = {};
|
|
||||||
}
|
|
||||||
for (const [k, v] of Object.entries(arg)) {
|
|
||||||
if (k === "children") continue;
|
|
||||||
meta.scope[k] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return modifyChildIndex(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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const cachedLocal = {} as Record<string, Record<string, any>>;
|
|
||||||
const cachedPath = {} as Record<string, Record<string, any>>;
|
|
||||||
const cachedLayout = {} as Record<string, true>;
|
|
||||||
const createLocal = (
|
|
||||||
p: PG,
|
|
||||||
id: string,
|
|
||||||
_existingScopeIndex?: Record<string, any>
|
|
||||||
) => {
|
|
||||||
const Local = ({
|
|
||||||
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 = p.treeMeta[id];
|
|
||||||
const [_, set] = useState({});
|
|
||||||
|
|
||||||
let scope = null as any;
|
|
||||||
if (!local_id) {
|
|
||||||
if (!meta.scope) {
|
|
||||||
meta.scope = {};
|
|
||||||
}
|
|
||||||
scope = meta.scope;
|
|
||||||
} else {
|
|
||||||
if (!meta.indexedScope) {
|
|
||||||
meta.indexedScope = {};
|
|
||||||
}
|
|
||||||
if (!meta.indexedScope[local_id]) meta.indexedScope[local_id] = {};
|
|
||||||
scope = meta.indexedScope[local_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
const render = () => {
|
|
||||||
if (!local_id) {
|
|
||||||
if (meta.render) meta.render();
|
|
||||||
else p.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 = p.page?.id || "";
|
|
||||||
const itemid = meta.item.id + (local_id ? `_${local_id}` : "");
|
|
||||||
|
|
||||||
if (meta.isLayout) {
|
|
||||||
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.isLayout) {
|
|
||||||
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 modifyChildIndex(children, scopeIndex);
|
|
||||||
}
|
|
||||||
return children;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Local;
|
|
||||||
};
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import { w } from "../../utils/types/general";
|
|
||||||
import { Loader } from "../live/logic/global";
|
|
||||||
|
|
||||||
const cache = {
|
|
||||||
site: null as any,
|
|
||||||
pages: [] as any,
|
|
||||||
api: null,
|
|
||||||
npm_pages: [] as string[],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mobileLoader: Loader = {
|
|
||||||
async site(p, id) {
|
|
||||||
const res = (await load(`/content/site/site.json`)) as any;
|
|
||||||
cache.site = res;
|
|
||||||
|
|
||||||
const pages = (await load(`/content/site/pages.json`)) as any;
|
|
||||||
cache.pages = pages;
|
|
||||||
|
|
||||||
const npm_pages = (await load(`/content/site/npm_pages.json`)) as any;
|
|
||||||
cache.npm_pages = npm_pages;
|
|
||||||
|
|
||||||
w.serverurl = res.config.api_url;
|
|
||||||
w.apiurl = res.config.api_url;
|
|
||||||
|
|
||||||
w.prasiApi = {
|
|
||||||
[res.config.api_url]: { apiEntry: res.api },
|
|
||||||
};
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
async comp(p, id) {
|
|
||||||
const comp = (await load(`/content/comps/${id}.json`)) as any;
|
|
||||||
p.comps.all[id] = comp;
|
|
||||||
return comp;
|
|
||||||
},
|
|
||||||
npm(p, type, id) {
|
|
||||||
if (type === "site") return `/content/npm/site/index.js`;
|
|
||||||
if (cache.npm_pages.includes(id)) {
|
|
||||||
return `/content/npm/pages/${id}/index.js`;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
async page(p, id) {
|
|
||||||
const page = cache.pages.find((x: any) => x.id === id);
|
|
||||||
if (page && !page.content_tree) {
|
|
||||||
const res = (await load(`/content/pages/${id}.json`)) as any;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
async pages(p, id) {
|
|
||||||
return cache.pages;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const load = async (url: string) => {
|
|
||||||
const res = await fetch(`${(w as any).mobilepath}${url}`);
|
|
||||||
try {
|
|
||||||
const text = await res.text();
|
|
||||||
const json = JSON.parse(text);
|
|
||||||
return json;
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import { w } from "../../utils/types/general";
|
|
||||||
import { Loader } from "../live/logic/global";
|
|
||||||
|
|
||||||
const base = `/_web/${(window as any).id_site}`;
|
|
||||||
|
|
||||||
const cache = { site: null as any, pages: [] as any, api: null };
|
|
||||||
|
|
||||||
export const siteLoader: Loader = {
|
|
||||||
async site(p, id) {
|
|
||||||
const res = (await load(`/site?prod=1`)) as {
|
|
||||||
site: any;
|
|
||||||
pages: any;
|
|
||||||
api: any;
|
|
||||||
};
|
|
||||||
cache.site = res.site;
|
|
||||||
cache.pages = res.pages;
|
|
||||||
cache.api = res.api;
|
|
||||||
|
|
||||||
w.serverurl = res.site.config.api_url;
|
|
||||||
w.apiurl = res.site.config.api_url;
|
|
||||||
|
|
||||||
w.prasiApi = {
|
|
||||||
[res.site.config.api_url]: { apiEntry: res.api },
|
|
||||||
};
|
|
||||||
|
|
||||||
return res.site;
|
|
||||||
},
|
|
||||||
async comp(p, id) {
|
|
||||||
const comp = (await load(`/comp/${id}`)) as any;
|
|
||||||
p.comps.all[id] = comp;
|
|
||||||
return comp;
|
|
||||||
},
|
|
||||||
npm(p, type, id) {
|
|
||||||
if (type === "site") return `/_web/${cache.site.id}/npm-site/site.js`;
|
|
||||||
return `/_web/${cache.site.id}/npm-page/${id}/page.js`;
|
|
||||||
},
|
|
||||||
async page(p, id) {
|
|
||||||
const page = cache.pages.find((x: any) => x.id === id);
|
|
||||||
if (page && !page.content_tree) {
|
|
||||||
const res = (await load(`/page/${id}`)) as any;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
async pages(p, id) {
|
|
||||||
return cache.pages;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const load = async (url: string) => {
|
|
||||||
const res = await fetch(`${base}${url}`);
|
|
||||||
try {
|
|
||||||
const text = await res.text();
|
|
||||||
const json = JSON.parse(text);
|
|
||||||
return json;
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import { FC, useState } from "react";
|
|
||||||
import { createRoot } from "react-dom/client";
|
|
||||||
import { GlobalContext, defineReact, defineWindow } from "web-utils";
|
|
||||||
import { siteLoader } from "./site-loader";
|
|
||||||
import { registerMobile } from "../live/logic/mobile";
|
|
||||||
import { mobileLoader } from "./mobile-loader";
|
|
||||||
|
|
||||||
const w = window as unknown as {
|
|
||||||
prasiContext: any;
|
|
||||||
rootRender: any;
|
|
||||||
mobile: any;
|
|
||||||
mobilepath?: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!w.mobile) {
|
|
||||||
w.mobile = registerMobile();
|
|
||||||
}
|
|
||||||
|
|
||||||
w.prasiContext = {
|
|
||||||
global: {},
|
|
||||||
render() {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Root: FC<{ url: URL; Live: any }> = ({ url, Live }) => {
|
|
||||||
const [_, render] = useState({});
|
|
||||||
w.prasiContext.render = () => {
|
|
||||||
render({});
|
|
||||||
};
|
|
||||||
|
|
||||||
const Provider = GlobalContext.Provider as FC<{ value: any; children: any }>;
|
|
||||||
return (
|
|
||||||
<Provider value={w.prasiContext}>
|
|
||||||
<Live
|
|
||||||
domain={url.host}
|
|
||||||
pathname={location.pathname}
|
|
||||||
loader={w.mobilepath ? mobileLoader : siteLoader}
|
|
||||||
/>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const div = document.getElementById("root");
|
|
||||||
if (div) {
|
|
||||||
const root = createRoot(div);
|
|
||||||
const url = new URL(location.href);
|
|
||||||
await defineWindow(false);
|
|
||||||
defineReact();
|
|
||||||
const { Live } = await import("../live/live");
|
|
||||||
root.render(<Root url={url} Live={Live} />);
|
|
||||||
if (document.body.classList.contains("opacity-0")) {
|
|
||||||
document.body.classList.remove("opacity-0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
@ -61,7 +61,7 @@ export type MPage = TypedDoc<{
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type IContent = ISection | IItem | IText;
|
export type IContent = ISection | IItem | IText;
|
||||||
export type MContent = MSection | MItem | MText;
|
export type MContent = MItem;
|
||||||
|
|
||||||
export type RenderContentProp = Partial<{
|
export type RenderContentProp = Partial<{
|
||||||
active: IContent | null;
|
active: IContent | null;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue