This commit is contained in:
Rizky 2023-12-06 11:59:22 +07:00
parent de6ab61756
commit 22ec9dca9f
4 changed files with 276 additions and 16 deletions

View File

@ -1,32 +1,57 @@
import { FC } from "react";
import { useGlobal } from "web-utils";
import { EDGlobal, EdMeta, active } from "../../logic/ed-global";
import { useGlobal, useLocal } from "web-utils";
import { IItem } from "../../../../utils/types/item";
import { FMCompDef } from "../../../../utils/types/meta-fn";
import { Menu, MenuItem } from "../../../../utils/ui/context-menu";
import { EDGlobal, EdMeta, active } from "../../logic/ed-global";
import { reset } from "./prop-instance/prop-reset";
import { EdPropInstanceText } from "./prop-instance/prop-text";
import { Loading } from "../../../../utils/ui/loading";
export const EdSidePropInstance: FC<{ meta: EdMeta }> = ({ meta }) => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({
rightClickEvent: null as any,
reset: { mprop: null as any, name: "" },
});
const item = meta?.item as IItem;
if (!item) return null;
let filtered = [] as FMCompDef[];
let filtered = [] as { mprop: FMCompDef; name: string }[];
const mprops = meta.mitem?.get("component")?.get("props");
if (mprops && meta.mitem) {
mprops.forEach((m, key) => {
filtered.push(m);
const comp_id = meta.mitem?.get("component")?.get("id") || "";
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);
if (!mprop) {
const json = m.toJSON();
const map = new Y.Map() as any;
syncronize(map, json);
mprops.set(key, map);
filtered.push({ mprop: map, name: key });
} else {
filtered.push({ mprop, name: key });
}
});
filtered = filtered.sort((a, b) => {
const aidx = a.get("idx") || 0;
const bidx = b.get("idx") || 0;
const aidx = a.mprop.get("idx") || 0;
const bidx = b.mprop.get("idx") || 0;
return aidx - bidx;
});
}
return (
<div className="flex flex-col text-[12px]">
<div className="flex border-b p-1 h-[35px] items-center bg-slate-50 justify-between select-none">
<div className="flex flex-1 flex-col text-[12px]">
<div className="flex border-b p-1 h-[27px] items-center bg-slate-50 justify-between select-none">
<div className="flex-1 overflow-hidden mr-2 text-ellipsis whitespace-nowrap">
{meta.item.name}
</div>
@ -55,6 +80,57 @@ export const EdSidePropInstance: FC<{ meta: EdMeta }> = ({ meta }) => {
Edit Component
</div>
</div>
<div className="flex flex-1 relative overflow-auto">
<div className={cx("absolute inset-0")}>
{local.rightClickEvent && (
<Menu
mouseEvent={local.rightClickEvent}
onClose={() => {
local.rightClickEvent = null;
local.render();
}}
>
<MenuItem
label="Reset"
onClick={() => {
if (local.reset.name) {
reset(p, comp_id, local.reset.mprop, local.reset.name);
}
}}
/>
<MenuItem label={"Edit Code"} onClick={() => {}} />
</Menu>
)}
{filtered.length === 0 && (
<div className="flex absolute inset-0 items-center justify-center">
No Prop Available
</div>
)}
{filtered.map(({ name, mprop }) => {
const type = mprop.get("meta")?.get("type") || "text";
return (
<div
key={name}
className="border-b text-[13px] relative"
onContextMenu={(e) => {
e.preventDefault();
local.reset = { mprop, name };
local.rightClickEvent = e;
local.render();
}}
>
<>
{type === "text" && (
<EdPropInstanceText mprop={mprop} name={name} />
)}
{type !== "text" && <div className="p-1">{name}</div>}
</>
</div>
);
})}
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,30 @@
import { FMCompDef } from "../../../../../utils/types/meta-fn";
import { PG } from "../../../logic/ed-global";
export const reset = (
p: PG,
comp_id: string,
mprop: FMCompDef,
name: string
) => {
if (comp_id) {
const ref = p.comp.list[comp_id];
if (ref.doc) {
const mcprops = ref.doc
.getMap("map")
.get("root")
?.get("component")
?.get("props");
const mcprop = mcprops?.get(name);
if (mcprop) {
mprop.doc?.transact(() => {
mprop.set("value", mcprop.get("value"));
mprop.set("valueBuilt", mcprop.get("valueBuilt"));
});
p.render();
}
}
}
};

View File

@ -0,0 +1,129 @@
import {
FC,
TextareaHTMLAttributes,
useCallback,
useEffect,
useRef,
} from "react";
import { FMCompDef } from "../../../../../utils/types/meta-fn";
import { useGlobal, useLocal } from "web-utils";
import { EDGlobal } from "../../../logic/ed-global";
import { Tooltip } from "../../../../../utils/ui/tooltip";
export const EdPropInstanceText: FC<{
name: string;
mprop: FMCompDef;
}> = ({ name, mprop }) => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({
value: "",
codeEditing: false,
timeout: null as any,
});
const val = mprop.get("value");
const valBuilt = mprop.get("valueBuilt");
useEffect(() => {
if (val) {
try {
eval(`local.value = ${valBuilt}`);
} catch (e) {}
} else {
local.value = "";
}
local.render();
}, [val, valBuilt]);
const label = (
<div className="pl-1 max-w-[50px] overflow-hidden text-ellipsis whitespace-nowrap">
{name}
</div>
);
return (
<div className="flex items-center">
{name.length > 8 ? (
<Tooltip content={name} placement="left" delay={100}>
{label}
</Tooltip>
) : (
label
)}
<AutoHeightTextarea
className="flex-1 outline-none border-l p-1 ml-1 overflow-hidden"
value={local.value}
spellCheck={false}
onChange={(e) => {
local.value = e.currentTarget.value;
local.render();
clearTimeout(local.timeout);
local.timeout = setTimeout(() => {
mprop.doc?.transact(() => {
mprop.set("value", `\`${local.value}\``);
mprop.set("valueBuilt", `\`${local.value}\``);
});
}, 1000);
}}
/>
</div>
);
};
export function AutoHeightTextarea({
minRows = 1,
...props
}: TextareaHTMLAttributes<HTMLTextAreaElement> & { minRows?: number }) {
const ref = useRef<HTMLTextAreaElement>(null);
const calculateAndSetHeight = useCallback(() => {
if (!ref.current) {
return;
}
const {
borderBottomWidth,
borderTopWidth,
boxSizing,
lineHeight,
paddingBottom,
paddingTop,
} = window.getComputedStyle(ref.current);
ref.current.style.height = lineHeight; // set height temporarily to a single row to obtain scrollHeight, disregarding empty space after text (otherwise, scrollHeight would be equal to the height of the element) - this solves auto-shrinking of the textarea (it's not needed for auto-growing it)
const { scrollHeight } = ref.current; // scrollHeight = content height + padding top + padding bottom
if (boxSizing === "border-box") {
const minHeight =
parseFloat(lineHeight) * minRows +
parseFloat(paddingTop) +
parseFloat(paddingBottom) +
parseFloat(borderTopWidth) +
parseFloat(borderBottomWidth);
const allTextHeight =
scrollHeight +
parseFloat(borderTopWidth) +
parseFloat(borderBottomWidth);
ref.current.style.height = `${Math.max(minHeight, allTextHeight)}px`;
} else if (boxSizing === "content-box") {
const minHeight = parseFloat(lineHeight) * minRows;
const allTextHeight =
scrollHeight - parseFloat(paddingTop) - parseFloat(paddingBottom);
ref.current.style.height = `${Math.max(minHeight, allTextHeight)}px`;
} else {
console.error("Unknown box-sizing value.");
}
}, [minRows]);
useEffect(() => {
calculateAndSetHeight();
}, [calculateAndSetHeight]);
const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> = (e) => {
calculateAndSetHeight();
if (props.onChange) {
props.onChange(e);
}
};
calculateAndSetHeight();
return <textarea {...props} onChange={handleChange} ref={ref} />;
}

View File

@ -10,7 +10,7 @@ import { HTML5Backend } from "react-dnd-html5-backend";
import { useGlobal, useLocal } from "web-utils";
import { IItem } from "../../../../utils/types/item";
import { FMCompDef } from "../../../../utils/types/meta-fn";
import { EDGlobal, EdMeta } from "../../logic/ed-global";
import { EDGlobal, EdMeta, active } from "../../logic/ed-global";
import { EdPropCompTreeItem, PropItem } from "./prop-master/tree-item";
import { propPopover } from "./prop-master/prop-form";
@ -54,17 +54,42 @@ export const EdSidePropComp: FC<{ meta: EdMeta }> = ({ meta }) => {
return (
<div className="flex flex-col text-[12px] flex-1">
<div className="flex border-b p-1 h-[35px] items-center bg-slate-50 justify-between select-none">
<div className="flex-1 overflow-hidden mr-2 text-ellipsis whitespace-nowrap">
{item.name}
</div>
<div
className="border px-1 cursor-pointer bg-white hover:bg-blue-100"
className="flex cursor-pointer items-center"
onClick={() => {
p.ui.side.prop = false;
p.render();
}}
>
Close
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.84182 3.13514C9.04327 3.32401 9.05348 3.64042 8.86462 3.84188L5.43521 7.49991L8.86462 11.1579C9.05348 11.3594 9.04327 11.6758 8.84182 11.8647C8.64036 12.0535 8.32394 12.0433 8.13508 11.8419L4.38508 7.84188C4.20477 7.64955 4.20477 7.35027 4.38508 7.15794L8.13508 3.15794C8.32394 2.95648 8.64036 2.94628 8.84182 3.13514Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
<div>Style</div>
</div>
<div
className="border px-1 cursor-pointer bg-white hover:bg-blue-100"
onClick={() => {
if (active.comp_id) {
active.comp_id = active.instance.comp_id || "";
active.item_id = active.instance.item_id || "";
active.instance.comp_id = "";
active.instance.item_id = "";
p.render();
}
}}
>
Back to Instance
</div>
</div>
<div className="flex flex-1 relative overflow-auto">