prasi-bun/app/web/src/nova/ed/panel/side/prop-instance.tsx

342 lines
10 KiB
TypeScript

import { FC, MouseEvent } from "react";
import { useGlobal, useLocal } from "web-utils";
import { IItem } from "../../../../utils/types/item";
import { FMCompDef, FNCompDef } from "../../../../utils/types/meta-fn";
import { Menu, MenuItem } from "../../../../utils/ui/context-menu";
import { EDGlobal, IMeta, PG, active } from "../../logic/ed-global";
import { createEditScript } from "./prop-instance/edit-script";
import { EdPropInstanceCode } from "./prop-instance/prop-code";
import { EdPropInstanceOptions } from "./prop-instance/prop-option";
import { reset } from "./prop-instance/prop-reset";
import { EdPropInstanceText } from "./prop-instance/prop-text";
import { EdStyleAll } from "./style/side-all";
export const EdSidePropInstance: FC<{ meta: IMeta }> = ({ meta }) => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal(
{
rightClickEvent: null as any,
pick: { mprop: null as any, name: "" },
showJSX: false,
expand: {
prop: false,
style: false,
},
},
() => {
local.expand.prop = true;
if (localStorage.getItem("prop-instance-show-prop") === "false") {
local.expand.prop = false;
}
local.expand.style = true;
if (localStorage.getItem("prop-instance-show-style") === "false") {
local.expand.style = false;
}
local.render();
}
);
let _meta = meta;
if (active.comp_id) {
if (p.comp.list[active.comp_id]) {
_meta = getCompMeta(p, meta);
}
}
const item = _meta?.item as IItem;
if (!item) return <>Warning: Item not found</>;
if (!_meta.mitem) return <>Warning: MItem Not Found</>;
let filtered = [] as { mprop: FMCompDef; cprop: FNCompDef; name: string }[];
const mprops = _meta.mitem?.get("component")?.get("props");
const comp_id = _meta.mitem?.get("component")?.get("id") || "";
if (!p.comp.list[comp_id]) return <>Warning: Component not found</>;
const mcprops = p.comp.list[comp_id].doc
.getMap("map")
.get("root")
?.get("component")
?.get("props");
if (mprops && _meta.mitem && mcprops) {
mcprops.forEach((m, key) => {
let mprop = mprops.get(key);
const cprop = m.toJSON() as any;
const type = m.get("meta")?.get("type") || "text";
const visible = m?.get("visible") || "";
if (visible && visible !== "true") {
try {
const arg: any = {};
if (meta.item.script?.props) {
for (const [k, v] of Object.entries(meta.item.script?.props)) {
eval(`arg.${k} = ${v.value}`);
}
}
const visible_fn = new Function(
...Object.keys(arg),
`return ${visible}`
);
const res = visible_fn(...Object.values(arg));
if (!res) {
return;
}
} catch (e) {
return;
}
}
if (!local.showJSX && type === "content-element") {
return;
}
if (!mprop) {
const json = m.toJSON();
const map = new Y.Map() as any;
syncronize(map, json);
mprops.set(key, map);
filtered.push({ mprop: map, cprop, name: key });
} else {
filtered.push({ mprop, cprop, name: key });
}
});
filtered = filtered.sort((a, b) => {
const aidx = a.mprop.get("idx") || 0;
const bidx = b.mprop.get("idx") || 0;
return aidx - bidx;
});
}
const expandable = item.component?.useStyle;
const show_prop = !expandable || (expandable && local.expand.prop);
const show_style = !expandable || (expandable && local.expand.style);
return (
<div className="flex flex-1 flex-col text-[12px]">
<div
className={cx(
"flex border-b p-1 h-[28px] items-center bg-slate-50 justify-between select-none",
expandable && "cursor-pointer hover:bg-blue-100"
)}
onClick={() => {
if (expandable) {
local.expand.prop = !local.expand.prop;
localStorage.setItem(
"prop-instance-show-prop",
JSON.stringify(local.expand.prop)
);
local.render();
}
}}
>
{expandable && <>{!local.expand.prop ? <TriRight /> : <TriDown />}</>}
<div className="flex-1 overflow-hidden mr-2 text-ellipsis whitespace-nowrap">
{expandable ? _meta.item.name : `Props`}
</div>
{p.ui.comp_editable && (
<div
className="border px-1 cursor-pointer bg-white hover:bg-blue-100"
onClick={() => {
const item = _meta.item as IItem;
const comp_id = item.component?.id;
if (comp_id) {
if (!p.comp.list[comp_id]) return;
active.instance.item_id = item.id;
active.instance.comp_id = active.comp_id;
active.comp_id = comp_id || "";
const root = p.comp.list[comp_id].tree.find(
(e) => e.parent === "root"
);
if (root && typeof root.id === "string") {
active.item_id = root.id || "";
}
p.render();
}
}}
>
Edit Component
</div>
)}
</div>
<div className="flex flex-1 relative overflow-y-auto overflow-x-hidden">
<div className={cx("absolute inset-0")}>
{local.rightClickEvent && (
<Menu
mouseEvent={local.rightClickEvent}
onClose={() => {
local.rightClickEvent = null;
local.render();
}}
>
<MenuItem
label="Reset"
onClick={() => {
if (local.pick.name) {
reset(p, comp_id, local.pick.mprop, local.pick.name);
}
}}
/>
<MenuItem
label={"Edit Code"}
onClick={createEditScript(
p,
"value",
local.pick.mprop,
local.pick.name
)}
/>
</Menu>
)}
{show_prop && (
<>
{filtered.length === 0 && (
<div className="flex absolute inset-0 items-center justify-center">
No Prop Available
</div>
)}
{filtered.map(({ name, mprop, cprop }) => {
const type = cprop.meta?.type || "text";
let hasCode = false;
const value = mprop.get("value") || "";
if (
!!value &&
(![`"`, "'", "`"].includes(value[0]) ||
![`"`, "'", "`"].includes(value[value.length - 1]))
) {
hasCode = true;
}
if (value.length > 100) {
hasCode = true;
}
const labelClick = (e: MouseEvent<HTMLDivElement>) => {
e.preventDefault();
local.pick = { mprop, name };
local.rightClickEvent = e;
local.render();
};
return (
<div
key={name}
className="border-b text-[13px] relative hover:bg-orange-100 cursor-default"
onContextMenu={labelClick}
>
{hasCode ? (
<>
<EdPropInstanceCode
mprop={mprop}
name={name}
label={cprop.label}
labelClick={labelClick}
onEditCode={createEditScript(p, "value", mprop, name)}
/>
</>
) : (
<>
{type === "text" && (
<EdPropInstanceText
mprop={mprop}
label={cprop.label}
name={name}
labelClick={labelClick}
/>
)}
{type === "option" && (
<EdPropInstanceOptions
mprop={mprop}
cprop={cprop}
name={name}
labelClick={labelClick}
/>
)}
{type === "content-element" && (
<div className="min-h-[28px] px-1 flex items-center">
{name}
</div>
)}
</>
)}
</div>
);
})}
</>
)}
{item.component?.useStyle && (
<>
<div
className={cx(
"flex border-b p-1 h-[28px] items-center bg-slate-50 justify-between select-none",
expandable && "cursor-pointer hover:bg-blue-100"
)}
onClick={() => {
if (expandable) {
local.expand.style = !local.expand.style;
localStorage.setItem(
"prop-instance-show-style",
JSON.stringify(local.expand.style)
);
local.render();
}
}}
>
{expandable && (
<>{!local.expand.style ? <TriRight /> : <TriDown />}</>
)}
<div className="flex-1 overflow-hidden mr-2 text-ellipsis whitespace-nowrap">
Component Style
</div>
</div>
{show_style && <EdStyleAll />}
</>
)}
</div>
</div>
</div>
);
};
const getCompMeta = (p: PG, imeta: IMeta) => {
let meta = null as unknown as IMeta;
return imeta;
};
const TriRight = () => (
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M6 11L6 4L10.5 7.5L6 11Z" fill="currentColor"></path>
</svg>
);
const TriDown = () => (
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M4 6H11L7.5 10.5L4 6Z" fill="currentColor"></path>
</svg>
);