190 lines
5.9 KiB
TypeScript
190 lines
5.9 KiB
TypeScript
import { FC, useEffect } from "react";
|
|
import { useGlobal } from "web-utils";
|
|
import { produceCSS } from "../../../../../utils/css/gen";
|
|
import { IItem } from "../../../../../utils/types/item";
|
|
import { IText } from "../../../../../utils/types/text";
|
|
import { EDGlobal, PG, active } from "../../../logic/ed-global";
|
|
import { loadComponent } from "../../../logic/tree/sync-walk";
|
|
import { EdCompPreviewTree } from "./comp-preview-tree";
|
|
import { compPicker, reloadCompPicker } from "./comp-reload";
|
|
|
|
export const EdCompPreview = () => {
|
|
const p = useGlobal(EDGlobal, "EDITOR");
|
|
const comp_id = p.ui.popup.comp.preview_id;
|
|
const ref = p.comp.list[comp_id];
|
|
|
|
const item = ref?.doc?.getMap("map").get("root")?.toJSON() as
|
|
| IItem
|
|
| undefined;
|
|
|
|
useEffect(() => {
|
|
if (!p.comp.list[comp_id] && !!comp_id) {
|
|
loadComponent(p, comp_id).then(() => {
|
|
p.render();
|
|
});
|
|
}
|
|
}, [comp_id]);
|
|
|
|
const isTrashed = !!compPicker.trash.find((e) => e.id === comp_id);
|
|
|
|
return (
|
|
<div className="flex flex-1 flex-col items-stretch overflow-auto border-l">
|
|
{comp_id && item && (
|
|
<div className="flex px-1 py-1 border-b h-[30px]">
|
|
<div className="flex flex-1 items-center">
|
|
<div>Preview</div>
|
|
<div className="text-[8px] font-mono text-slate-500 mx-1">
|
|
{comp_id}
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-1 justify-end">
|
|
<div
|
|
className={cx(
|
|
"cursor-pointer transition-all flex items-center border px-1 text-white hover:opacity-50 mr-1",
|
|
!isTrashed
|
|
? "border-green-700 bg-green-700"
|
|
: "border-purple-700 bg-purple-700"
|
|
)}
|
|
onClick={async (e) => {
|
|
e.stopPropagation();
|
|
|
|
if (isTrashed) {
|
|
p.ui.popup.comp_group = {
|
|
mouse_event: e,
|
|
async on_pick(group_id) {
|
|
await db.component.update({
|
|
where: { id: comp_id },
|
|
data: { id_component_group: group_id },
|
|
});
|
|
await reloadCompPicker(p);
|
|
p.render();
|
|
},
|
|
};
|
|
} else {
|
|
if (p.ui.popup.comp.open) {
|
|
p.ui.popup.comp.open(item.id);
|
|
}
|
|
p.ui.popup.comp.open = null;
|
|
|
|
active.item_id = compPicker.active_id;
|
|
compPicker.active_id = "";
|
|
}
|
|
|
|
p.render();
|
|
}}
|
|
>
|
|
{isTrashed ? "Restore Component" : "Select Component"}
|
|
</div>
|
|
<div
|
|
className="cursor-pointer transition-all bg-white flex items-center border px-1 hover:border-red-300 hover:bg-red-100"
|
|
onClick={async (e) => {
|
|
e.stopPropagation();
|
|
if (isTrashed) {
|
|
if (confirm("Permanently delete this component?")) {
|
|
await db.component.delete({
|
|
where: { id: p.ui.popup.comp.preview_id },
|
|
});
|
|
const idx =
|
|
compPicker.tree.findIndex((e) => e.id === comp_id) + 1;
|
|
|
|
if (idx >= 0 && compPicker.tree[idx])
|
|
p.ui.popup.comp.preview_id = compPicker.tree[idx]
|
|
.id as any;
|
|
|
|
compPicker.tree = compPicker.tree.filter(
|
|
(e) => e.id !== comp_id
|
|
);
|
|
p.render();
|
|
}
|
|
} else {
|
|
if (confirm("Move component to trash?")) {
|
|
await db.component.update({
|
|
where: { id: comp_id },
|
|
data: { id_component_group: compPicker.trash_id },
|
|
});
|
|
await reloadCompPicker(p);
|
|
p.render();
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
<DeleteIcon />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div className="relative flex flex-1">
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
{comp_id ? (
|
|
<>
|
|
{item && ref ? (
|
|
<div className="flex-1 flex relative w-full h-full">
|
|
<EdCompPreviewTree tree={ref.tree} />
|
|
<div
|
|
className={cx(
|
|
"flex-1 flex flex-col relative",
|
|
css`
|
|
content: contain;
|
|
`
|
|
)}
|
|
>
|
|
<CItem p={p} item={item} />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div>Loading...</div>
|
|
)}
|
|
</>
|
|
) : (
|
|
<>
|
|
Select component
|
|
<br /> to preview
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const CItem: FC<{ item: IItem | IText; p: PG }> = ({ item, p }) => {
|
|
const className = produceCSS(item, {
|
|
mode: p.mode,
|
|
});
|
|
|
|
if (item.type === "item") {
|
|
<div className={className}>
|
|
{item.type === "item" &&
|
|
item.childs.map((e) => {
|
|
return <CItem key={e.id} item={e} p={p} />;
|
|
})}
|
|
</div>;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={className}
|
|
dangerouslySetInnerHTML={
|
|
item.type === "text" ? { __html: item.html } : undefined
|
|
}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const DeleteIcon = () => (
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="12"
|
|
height="12"
|
|
fill="none"
|
|
viewBox="0 0 15 15"
|
|
>
|
|
<path
|
|
fill="currentColor"
|
|
fillRule="evenodd"
|
|
d="M5.5 1a.5.5 0 000 1h4a.5.5 0 000-1h-4zM3 3.5a.5.5 0 01.5-.5h8a.5.5 0 010 1H11v8a1 1 0 01-1 1H5a1 1 0 01-1-1V4h-.5a.5.5 0 01-.5-.5zM5 4h5v8H5V4z"
|
|
clipRule="evenodd"
|
|
></path>
|
|
</svg>
|
|
);
|